β οΈ Svelte 4 β 5 Migration Cheat Sheet | Knowledge Gap Bridge
π― Core Concept
Runes = compiler instructions starting with $ that make reactivity explicit
π Reactivity Syntax Changes
State Declaration
// β Svelte 4 - implicit reactivity
let count = 0;
// β
Svelte 5 - explicit $state rune
let count = $state(0);Derived Values
// β Svelte 4
$: doubled = count * 2;
// β
Svelte 5
const doubled = $derived(count * 2);
// Complex derivations
const total = $derived.by(() => {
return items.reduce((sum, item) => sum + item.price, 0);
});Side Effects
// β Svelte 4
$: {
console.log('count changed:', count);
document.title = `Count: ${count}`;
}
// β
Svelte 5
$effect(() => {
console.log('count changed:', count);
document.title = `Count: ${count}`;
});π¦ Component Props
Basic Props
// β Svelte 4
<script>
export let name = 'default';
export let required;
</script>
// β
Svelte 5
<script>
let { name = 'default', required } = $props();
</script>Advanced Props Patterns
// Renaming (e.g., reserved words)
let { class: className, ...rest } = $props();
// All props without destructuring
let props = $props();
// TypeScript typing
let { name, age }: { name: string; age: number } = $props();πͺ Event Handlers
Element Events
// β Svelte 4 - directive syntax
<button on:click={handleClick}>Click</button>
<button on:click|preventDefault={submit}>Submit</button>
// β
Svelte 5 - property syntax (no colon!)
<button onclick={handleClick}>Click</button>
<button onclick={(e) => { e.preventDefault(); submit(e); }}>Submit</button>Event Modifiers β Manual Handling
// β Svelte 4 modifiers
<button on:click|stopPropagation|once={handler}>
// β
Svelte 5 - handle manually
<script>
function handler(e) {
e.stopPropagation();
// ... logic
}
</script>
<button onclick={handler}>Component Events β Callback Props
// β Svelte 4 - createEventDispatcher
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function notify() { dispatch('message', { text: 'hi' }); }
</script>
// β
Svelte 5 - callback props
<script>
let { onMessage } = $props();
function notify() { onMessage?.({ text: 'hi' }); }
</script>π° Slots β Snippets
Default Content
// β Svelte 4
<slot />
<slot>Fallback</slot>
// β
Svelte 5
{@render children?.()}
{@render children?.() ?? <p>Fallback</p>}Named Slots β Named Snippets
// β Svelte 4 Parent
<Card>
<span slot="header">Title</span>
<p>Content</p>
</Card>
// β
Svelte 5 Parent
<Card>
{#snippet header()}
<span>Title</span>
{/snippet}
<p>Content</p>
</Card>
// β
Svelte 5 Child (Card.svelte)
<script>
let { header, children } = $props();
</script>
<div class="header">{@render header?.()}</div>
<div class="body">{@render children?.()}</div>π Component Mounting (main.ts)
App Bootstrap
// β Svelte 4 - class instantiation
import App from "./App.svelte"
const app = new App({
target: document.getElementById("app")!,
props: { name: "world" },
})
// β
Svelte 5 - mount function
import { mount } from "svelte"
import App from "./App.svelte"
const app = mount(App, {
target: document.getElementById("app")!,
props: { name: "world" },
})Cleanup
// β Svelte 4
app.$destroy()
// β
Svelte 5
import { unmount } from "svelte"
unmount(app)β οΈ Common Pitfalls
π« Donβt Use $effect for Derived State
// β WRONG - causes unnecessary reruns
let doubled = $state(0);
$effect(() => { doubled = count * 2; });
// β
CORRECT - use $derived
const doubled = $derived(count * 2);π« Event Handler Syntax
// β WRONG - Svelte 4 syntax causes errors
<button on:click={handler}>
// β
CORRECT - no colon
<button onclick={handler}>π« Self-Closing Non-Void Elements
// β WRONG - causes parser errors
<div />
<textarea />
<button />
// β
CORRECT - explicit close tags
<div></div>
<textarea></textarea>
<button></button>π« {@html} Syntax
// β
Still valid in Svelte 5
{@html rawHtmlContent}π Quick Reference Table
| Svelte 4 | Svelte 5 | Notes |
|---|---|---|
let x = 0 | let x = $state(0) | Explicit reactivity |
$: y = x * 2 | const y = $derived(x * 2) | Computed values |
$: { sideEffect() } | $effect(() => { sideEffect() }) | Side effects |
export let prop | let { prop } = $props() | Component props |
on:click={fn} | onclick={fn} | Remove colon |
on:click|preventDefault | Manual e.preventDefault() | No modifiers |
createEventDispatcher | Callback props onEvent | Component events |
<slot /> | {@render children?.()} | Content projection |
<slot name="x" /> | {@render x?.()} | Named content |
new App({target}) | mount(App, {target}) | Instantiation |
app.$destroy() | unmount(app) | Cleanup |
π§ Package Versions (Compatibility)
{
"devDependencies": {
"svelte": "^5.0.0",
"vite": "^6.0.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0"
}
}β‘
@sveltejs/vite-plugin-svelte@5requires Vite 6 β‘ Svelte 5 runes require the Svelte 5 compiler