Derived values and createMemo
A derived value is something computed from other data: the number of unfinished tasks, a filtered list, a full name from first and last names. There is no need to store it in a separate signal — it is enough to describe how to get it.
A derived function
The simplest way is an ordinary arrow function that reads signals:
// src/components/Counter.jsx
import { createSignal } from 'solid-js';
export default function Counter() {
const [count, setCount] = createSignal(0);
const doubled = () => count() * 2;
return (
<div>
<p>Tasks: {count()}, doubled: {doubled()}</p>
<button onClick={() => setCount(count() + 1)}>Add</button>
</div>
);
}On each click both numbers update. doubled is a function, so, like a signal, it is called: doubled(). The call inside JSX reads count(), and Solid tracks that dependency.
createMemo
A derived function runs every time it is called. If the calculation is heavy or the result is used in many places, cache it with createMemo:
// src/components/TodoStats.jsx
import { createSignal, createMemo } from 'solid-js';
export default function TodoStats() {
const [todos, setTodos] = createSignal([
{ id: 1, title: 'Buy milk', done: true },
{ id: 2, title: 'Call Oleg', done: false },
]);
const left = createMemo(() => todos().filter((t) => !t.done).length);
const add = () => {
setTodos([...todos(), { id: Date.now(), title: 'New task', done: false }]);
};
return (
<div>
<p>Left: {left()}</p>
<p>Total: {todos().length}</p>
<button onClick={add}>Add</button>
</div>
);
}The page shows "Left: 1" and "Total: 2"; each click increases both numbers. createMemo recomputes the filter only when todos changes, and between changes it returns the saved result.
Which to choose
| Approach | When it fits |
|---|---|
const x = () => ... | A simple calculation used in one or two places |
createMemo(() => ...) | A heavy calculation or many readers |
Start with a function. Switch to createMemo when you see the same thing being computed many times.
A memo also returns a function
The result of createMemo is read by calling it, like a signal: left(). You can pass it into props and use it inside other memos — the dependencies chain up automatically.
Common mistake
Duplicating a derived value in a separate signal and updating it by hand:
// src/components/TodoStats.jsx
import { createSignal, createEffect } from 'solid-js';
export default function TodoStats() {
const [todos, setTodos] = createSignal([]);
// Unnecessary: a second signal and an effect to sync it
const [total, setTotal] = createSignal(0);
createEffect(() => setTotal(todos().length));
return <p>Total: {total()}</p>;
}This code is more complex and can show a stale value. const total = () => todos().length is enough.