Skip to content

Same vs Different: Reference Table

This page is your cheat sheet. Every major React concept maps to a Svelte equivalent — many are nearly identical in intent, different only in syntax. A handful require a genuine mental-model shift. Both kinds are listed here.

ReactSvelte 5Notes
function Component().svelte fileThe file is the component — no function wrapper
JSX return (...)Markup bodyNo return, no parens — the template is the file body
export default functionFile is the exportNothing to export manually
className="foo"class="foo"Real HTML attributes — no renaming
Function prop arguments ({ name })let { name } = $props()Rune-based prop declaration
useState(init)$state(init)Direct mutation replaces setter functions
useMemo(() => x, [deps])$derived(x)Auto-tracked, no dependency array
useEffect(() => {}, [deps])$effect(() => {})Auto-tracked, cleanup via return function
useCallback(fn, [deps])Plain function (no equivalent needed)Functions are not re-created on each update
useRef(null)let el = $state(null) / bind:this={el}bind:this binds to the DOM element
useContext / createContextSvelte stores / setContext / getContextContext API exists; stores preferred for reactivity
children prop{@render children()} snippetSnippets are typed template fragments, not values
Named slot / render prop{#snippet name()} / {@render name()}More explicit and type-safe than render props
onClickonclickLowercase, plain HTML event attributes
onChangeoninputoninput fires on every keystroke; onchange fires on blur
onKeyDownonkeydownAll event names are lowercase in Svelte 5
<input value={x} onChange={fn}bind:value={x}Two-way binding replaces the controlled-input pattern
CSS Modules / styled-components<style> blockAuto-scoped, zero config
clsx(a, { b: cond })class:b={cond}Built-in conditional class directive
React Router / next/linkSvelteKit <a href> / goto()SvelteKit handles routing; plain anchors work
useNavigateimport { goto } from '$app/navigation'SvelteKit navigation API
useSearchParamsimport { page } from '$app/stores'$page.url.searchParams
<Suspense> / lazy{#await} blockBuilt into the template language
React.memoNot neededSvelte does not re-run the whole component on updates
Create React AppVite (+ @sveltejs/vite-plugin-svelte)CRA is deprecated; Vite is the standard
Next.jsSvelteKitFull-stack framework: SSR, routing, API routes
React
// React — must be immutable
const [items, setItems] = React.useState([]);
function addItem(item) {
setItems([...items, item]); // spread, never push
}
Svelte
<script>
// Svelte — direct mutation is tracked
let items = $state([]);
function addItem(item) {
items.push(item); // push is fine — Svelte detects it
}
</script>

React hooks have strict rules: call at the top level, never inside conditions or loops, always in the same order. Svelte runes have none of these restrictions — you can call $state inside a condition or inside a loop. They are compiler directives, not function calls tracked by call order.

React
// React — hooks must be at the top level, always called
function Conditional({ show }) {
// RULE: can't put hooks inside an if — even though it's unused
const [x, setX] = React.useState(0);
if (!show) return null;
return <button onClick={() => setX(x + 1)}>{x}</button>;
}
Svelte
<script>
let { show } = $props();
// Runes can be declared anywhere — no ordering rules
let x = $state(0);
</script>
{#if show}
<button onclick={() => x++}>{x}</button>
{/if}
What is the Svelte 5 equivalent of React's useState?
Which React pattern does Svelte's "bind:value" replace?
Can you call a Svelte $state rune inside an if-block or loop?
What is the Svelte equivalent of Next.js?