Svelte and SvelteKit course Β· Module 3: Reactivity and Runes

Comparison: Old vs New Syntax

4 min read Β·
In this lesson8

Let us see how the old Svelte 4 syntax translates to Runes in Svelte 5. You will meet the old syntax in existing projects and older tutorials, so it is worth recognizing it - but write new code with runes. In every pair, Svelte 4 (legacy) comes first and Svelte 5 below it.

State

<!-- Svelte 4 (legacy) -->
<script>
  let count = 0;
  let items = [];
</script>
<!-- Svelte 5 with Runes -->
<script>
  let count = $state(0);
  let items = $state([]);
</script>

Reactive Declarations

<!-- Svelte 4 (legacy) -->
<script>
  let count = 0;
  $: doubled = count * 2;
  $: tripled = count * 3;
  $: total = doubled + tripled;
</script>
<!-- Svelte 5 with Runes -->
<script>
  let count = $state(0);
  let doubled = $derived(count * 2);
  let tripled = $derived(count * 3);
  let total = $derived(doubled + tripled);
</script>

Reactive Statements

<!-- Svelte 4 (legacy) -->
<script>
  let count = 0;
  $: console.log('Count:', count);
  $: {
    if (count > 10) {
      alert('A lot!');
    }
  }
</script>
<!-- Svelte 5 with Runes -->
<script>
  let count = $state(0);

  $effect(() => {
    console.log('Count:', count);
  });

  $effect(() => {
    if (count > 10) {
      alert('A lot!');
    }
  });
</script>

Props

<!-- Svelte 4 (legacy) -->
<script>
  export let name;
  export let count = 0;
</script>
<!-- Svelte 5 with Runes -->
<script>
  let { name, count = 0 } = $props();
</script>

Bindable Props

The Input.svelte component hands the typed value back to its parent through bind:value:

<!-- Svelte 4 (legacy) -->
<script>
  export let value;
</script>

<input bind:value>
<!-- Svelte 5 with Runes -->
<script>
  let { value = $bindable() } = $props();
</script>

<input bind:value>

Usage in the parent looks the same in both versions:

<script>
  import Input from './Input.svelte';

  let myValue = $state('');
</script>

<Input bind:value={myValue} />
<p>Typed: {myValue}</p>

Events

<!-- Svelte 4 (legacy) -->
<script>
  let count = 0;
</script>

<button on:click={() => count++}>Clicked {count} times</button>
<!-- Svelte 5 with Runes -->
<script>
  let count = $state(0);
</script>

<button onclick={() => count++}>Clicked {count} times</button>

When to Use Which?

SituationSvelte 4 (legacy)Svelte 5 Runes
Simple statelet x = 0let x = $state(0)
Computation$: y = x * 2let y = $derived(x * 2)
Effect$: { ... }$effect(() => { ... })
Propsexport let$props()
DOM eventson:click={fn}onclick={fn}
Component eventscreateEventDispatcherfunctions in props, e.g. onselect
Content from the parent<slot />{@render children()}

Compatibility Mode - Component by Component

Svelte 5 supports both syntaxes, but not in one file. A component that uses even a single rune runs in runes mode - then $: and export let are compile errors in it. A component without runes runs in compatibility (legacy) mode. So you can migrate gradually, file by file.

The old component keeps working unchanged:

<!-- OldCounter.svelte - Svelte 4 (legacy) -->
<script>
  let oldCount = 0;
  $: oldDoubled = oldCount * 2;
</script>

<button on:click={() => oldCount++}>
  Old counter: {oldCount} (x2 = {oldDoubled})
</button>

A new component next to it uses runes:

<!-- NewCounter.svelte - Svelte 5 with Runes -->
<script>
  let newCount = $state(0);
  let newDoubled = $derived(newCount * 2);
</script>

<button onclick={() => newCount++}>
  New counter: {newCount} (x2 = {newDoubled})
</button>

And the parent uses both as if nothing happened:

<!-- App.svelte -->
<script>
  import OldCounter from './OldCounter.svelte';
  import NewCounter from './NewCounter.svelte';
</script>

<OldCounter />
<NewCounter />

When you want to move a whole project to runes, the npx sv migrate svelte-5 command helps - it rewrites most of the code automatically.

Choose Runes for new projects - they are the present and the future of Svelte!

Docs entries used in this lesson

Spotted a mistake in this lesson?

Check yourself

Answer the questions from this lesson. Pick an answer to see right away whether it is correct.

  1. 1. What is the Svelte 5 equivalent of the old $: doubled = count * 2?

  2. 2. What is the Svelte 5 equivalent of the old 'export let name'?

Useful articles