⚠️ 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 4Svelte 5Notes
let x = 0let x = $state(0)Explicit reactivity
$: y = x * 2const y = $derived(x * 2)Computed values
$: { sideEffect() }$effect(() => { sideEffect() })Side effects
export let proplet { prop } = $props()Component props
on:click={fn}onclick={fn}Remove colon
on:click|preventDefaultManual e.preventDefault()No modifiers
createEventDispatcherCallback props onEventComponent 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@5 requires Vite 6 ⚑ Svelte 5 runes require the Svelte 5 compiler