Svelte 5 preview review

2023-12-27 #svelte

The Svelte team set up a miniature documentation site highlighting the new features coming to Svelte 5. Here's a quick review of my three favorites: runes, events, and snippets.

Runes

The star of the release will be runes. Currently, Svelte uses existing language features like export and labels for its reactive features.

<script>
// A prop
export let name;
 
// A reactive statement
$: console.log(name);
</script>
 
<p>Hello, {{ name }}!</p>

Svelte 5 is a departure from this setup, and has more explicit "runes" instead. Runes are function calls that get transformed by the compiler, and can be recognized by their $ prefix.

<script>
// A prop
let { name } = $props();
 
// A reactive statement
$effect(() => {
console.log(name);
});
</script>
 
<p>Hello, {{ name }}!</p>

I personally liked the (mis)use of existing JavaScript syntax for reactivity. However, it did come with weird edge cases which runes will iron out, and having one "system" for all reactivity features does make Svelte look more consistent. So all in all I understand why the team decided to pursue this direction, and I'm looking forward to trying it out.

Events

Event handles in Svelte have previously been prefixed with on:. This prefix has been removed and replaced with a syntax that mirrors props.

<!-- Svelte 4 -->
<button on:click={() => …}>
</button>
 
<!-- Svelte 5 -->
<button onclick={() => …}>
</button>

This makes component events more straightforward too as events are handled by callback props.

<!-- Svelte 4 -->
<Todo on:complete={() => …} />
 
<!-- Svelte 5 -->
<Todo complete={() => …} />
 
<!-- Todo.svelte -->
<script>
let { complete } = $props();
</script>
 
<input type="checkbox" oninput={complete} />

This opens up a world of possibilities, as you can now dynamically spread props and events on a component, which is something I sorely miss coming from React.

We often use form objects in projects that hold form state, and have methods to bind form values, handlers, and errors on a field component in a type-safe way. This is finally possible in Svelte.

<script>
let form = createForm({ name: 'Sebastian' });
</script>
 
<TextField {...form.field('name')} />

Snippets

This is an interesting one I haven't seen in other frameworks yet, but solves a problem I've had in other templating languages. Snippets allow you to reuse an HTML snippet within the same component file.

Here's an example that would be relevant to my blog: I have a PostHeader component that's used on the index page and post page. On the index page, the title should be wrapped in an h2 tag, on the post page in an h1.

{#if onPostPage}
<h1><a href="{{ permalink }}">{{ title }}</a><h1>
{:else}
<h2><a href="{{ permalink }}">{{ title }}</a><h2>
{/if}

This is a small example, but grows increasibly difficult to maintain as the inner contents of the dynamic tag grows. In Svelte 5, you can extract the inner contents to a snippet.

{#snippet title()}
<a href="{{ permalink }}">{{ title }}</a>
{/snippet}
 
{#if onPostPage}
<h1>{@render title()}<h1>
{:else}
<h2>{@render title()}<h2>
{/if}

Now there's no more repetition of the snippet contents.

There's more on the horizon for Svelte 5, read through the preview documentation for an introduction to all new features. Or if you're new to Svelte and want an introduction, check out Svelte by Example. (Which I'll update for Svelte 5 when it's released!)