Common Migration Mistakes

+15 Mana ✨

Learn from common mistakes during Svelte 5 migration.

1. Forgetting $state

svelte
<!-- āŒ Not reactive in runes mode -->
<script>
  let count = 0; // Forgot $state!
  $effect(() => console.log(count)); // Triggers $effect usage = runes mode
</script>

<!-- āœ… Correct -->
<script>
  let count = $state(0);
  $effect(() => console.log(count));
</script>

2. Assigning to $derived

svelte
<script>
  let count = $state(0);
  let doubled = $derived(count * 2);
  
  // āŒ Error! Can't assign to derived
  function reset() {
    doubled = 0;
  }
  
  // āœ… Change the source
  function reset() {
    count = 0;
  }
</script>

3. Missing Optional Chaining for Events

svelte
<script>
  let { onsubmit } = $props();
</script>

<!-- āŒ Crashes if onsubmit not provided -->
<button onclick={() => onsubmit()}>Submit</button>

<!-- āœ… Safe with optional chaining -->
<button onclick={() => onsubmit?.()}>Submit</button>

<!-- āœ… Or provide default -->
<script>
  let { onsubmit = () => {} } = $props();
</script>

4. Forgetting to Render Children

svelte
<script>
  let { children } = $props();
</script>

<!-- āŒ Children is a snippet, not a value -->
<div>{children}</div>

<!-- āœ… Must render it -->
<div>{@render children()}</div>

5. Array Mutation Without Reassignment

svelte
<script>
  let items = $state(['a', 'b']);
</script>

<!-- āŒ May not update (depends on context) -->
<button onclick={() => items.push('c')}>Add</button>

<!-- āœ… Reassignment always works -->
<button onclick={() => items = [...items, 'c']}>Add</button>

6. Effect Without Cleanup

svelte
<script>
  // āŒ Memory leak - interval never cleared
  $effect(() => {
    setInterval(() => count++, 1000);
  });
  
  // āœ… Return cleanup function
  $effect(() => {
    const i = setInterval(() => count++, 1000);
    return () => clearInterval(i);
  });
</script>

šŸ“– Migration pitfalls

āœ“ Completed