Effects: createEffect, onMount, onCleanup
An effect is code that does not build markup but does something "outside": writes to localStorage, changes the tab title, starts a timer. Solid runs an effect after rendering and repeats it when the signals it read change.
createEffect
// src/components/Counter.jsx
import { createSignal, createEffect } from 'solid-js';
export default function Counter() {
const [count, setCount] = createSignal(0);
createEffect(() => {
document.title = `Done: ${count()}`;
});
return <button onClick={() => setCount(count() + 1)}>Mark done</button>;
}The browser tab title changes on every click. No dependency list is needed as in React's useEffect: Solid sees for itself that the effect read count().
Saving tasks
An effect is handy for keeping the task list between page reloads:
// src/App.jsx
import { createSignal, createEffect } from 'solid-js';
export default function App() {
const saved = JSON.parse(localStorage.getItem('todos') ?? '[]');
const [todos, setTodos] = createSignal(saved);
createEffect(() => {
localStorage.setItem('todos', JSON.stringify(todos()));
});
const add = () => setTodos([...todos(), { id: Date.now(), title: 'Task', done: false }]);
return <button onClick={add}>Add ({todos().length})</button>;
}Click a few times, then reload the page — the number on the button stays.
onMount
onMount runs once, when the component's markup is already in the page:
// src/components/Clock.jsx
import { createSignal, onMount, onCleanup } from 'solid-js';
export default function Clock() {
const [time, setTime] = createSignal(new Date().toLocaleTimeString());
onMount(() => {
const timer = setInterval(() => setTime(new Date().toLocaleTimeString()), 1000);
onCleanup(() => clearInterval(timer));
});
return <p>It is {time()}</p>;
}The time on the page updates every second.
onCleanup
onCleanup registers a function that runs when the component is removed from the page or the effect runs again. In the example above it stops the timer; otherwise the timer would keep running after Clock is gone.
| Function | When it runs |
|---|---|
createEffect(fn) | After rendering and after every change of the signals it read |
onMount(fn) | Once, after insertion into the page |
onCleanup(fn) | When the component is removed or before the effect reruns |
Common mistake
Changing, inside an effect, the same signal the effect reads:
// src/components/Counter.jsx
import { createSignal, createEffect } from 'solid-js';
export default function Counter() {
const [count, setCount] = createSignal(0);
// Wrong: the effect triggers itself again and again
createEffect(() => setCount(count() + 1));
return <p>{count()}</p>;
}The effect loops. If a value is computed from other signals, use a derived function or createMemo, and keep effects for working with the outside world.