Skip to content

Lifecycle — onMount, onDestroy, tick

React uses a single useEffect for everything: mount, update, and cleanup. Svelte 5 separates concerns more explicitly. $effect handles reactive updates (covered in the previous lesson). onMount and onDestroy handle one-time setup and teardown. tick is a utility for flushing pending DOM updates before reading layout.

The complete React hooks ↔ Svelte 5 mapping table

Section titled “The complete React hooks ↔ Svelte 5 mapping table”
ReactSvelte 5Notes
useState(init)$state(init)Mutate directly, no setter
useMemo(() => expr, deps)$derived(expr)No deps array — compiler-tracked
useMemo(() => fn(), deps)$derived.by(() => fn())Multi-statement derived
useEffect(fn, deps)$effect(fn)No deps array — auto-tracked
useEffect(fn, [])onMount(fn)Run once after mount
useEffect(() => cleanup, [])onDestroy(fn)Run once on destroy
useRef(init)let variablePlain let for mutable non-reactive ref
useRef(null) + ref={el}bind:this={el}DOM element reference
useContext(Ctx)getContext(key)Read from closest ancestor provider
createContext() + ProvidersetContext(key, value)Provide context
Component props$props()Destructure with defaults
Callback props (lifting state)$bindable() + bind:Two-way without explicit callback
React
function Clock() {
const [time, setTime] = React.useState(new Date());
React.useEffect(() => {
const id = setInterval(() => setTime(new Date()), 1000);
return () => clearInterval(id);
}, []);
return <p>{time.toLocaleTimeString()}</p>;
}
Svelte
<script>
import { onMount } from 'svelte';
let time = $state(new Date());
onMount(() => {
const id = setInterval(() => { time = new Date(); }, 1000);
return () => clearInterval(id);
});
</script>
<p>{time.toLocaleTimeString()}</p>

onMount accepts a setup function and optionally returns a cleanup function, which runs on destroy — identical semantics to useEffect(fn, []).

React
function Analytics() {
React.useEffect(() => {
trackPageView('/dashboard');
return () => {
trackPageLeave('/dashboard');
};
}, []);
return <div>Dashboard</div>;
}
Svelte
<script>
import { onMount, onDestroy } from 'svelte';
onMount(() => trackPageView('/dashboard'));
onDestroy(() => trackPageLeave('/dashboard'));
</script>
<div>Dashboard</div>

Splitting mount and destroy into two calls is more readable when the setup and teardown are unrelated.

tick() returns a promise that resolves after Svelte has finished updating the DOM. Use it when you need to read layout (e.g. element dimensions, focus) immediately after a reactive change — equivalent to await flushSync + requestAnimationFrame in React.

React
function AutoScroll() {
const ref = React.useRef(null);
const [items, setItems] = React.useState(['a', 'b', 'c']);
function addItem() {
setItems(prev => [...prev, String.fromCharCode(97 + prev.length)]);
// Must use useEffect to scroll after render
}
React.useEffect(() => {
ref.current?.scrollIntoView({ block: 'end' });
}, [items]);
return (
<ul>
{items.map(i => <li key={i}>{i}</li>)}
<li ref={ref} />
</ul>
);
}
Svelte
<script>
import { tick } from 'svelte';
let items = $state(['a', 'b', 'c']);
let endEl;
async function addItem() {
items.push(String.fromCharCode(97 + items.length));
await tick();
endEl?.scrollIntoView({ block: 'end' });
}
</script>
<ul>
{#each items as item}
<li>{item}</li>
{/each}
<li bind:this={endEl}></li>
</ul>
<button onclick={addItem}>Add</button>
What is the Svelte 5 equivalent of useEffect(fn, []) — run once after mount?
You want to get a reference to a DOM element in Svelte 5. What do you use?
When is tick() useful?
What is the Svelte 5 equivalent of useContext(MyContext)?