Article
Exploring Svelte 5: What’s New and How to Make the Most of It
Explore Svelte 5's new features: reactivity with runes, modular snippets, improved animations, debugging tools, and more in this comprehensive guide!
Svelte has always been a game-changer in the web development world, offering a unique, compiler-first approach to building user interfaces. With the release of Svelte 5, the framework has pushed boundaries even further, redefining how developers think about reactivity, state management, and component composition.
In this article, we’ll dive deep into what’s new in Svelte 5, walk through practical examples, and explain how these updates improve your development experience. Whether you’re just starting out or a seasoned Svelte developer, there’s plenty to learn and love here.
Why Developers Love Svelte: A Quick Refresher
Before diving into Svelte 5, let’s take a moment to appreciate what makes Svelte special. Unlike other frameworks like React or Vue, Svelte doesn’t rely on a virtual DOM. Instead, Svelte compiles your components into highly optimized JavaScript during build time. The result? Faster load times, less overhead, and a cleaner, more intuitive developer experience.
If you’ve used Svelte before, you’re already familiar with its simplicity and power. But with Svelte 5, things just got even better.
What’s New in Svelte 5?
Let’s start with a high-level overview of the key new features in Svelte 5:
- Runes: A revolutionary reactivity model that makes state management explicit and predictable.
- Snippets: Inline reusable components for cleaner and modular code.
- Improved state management tools: New ways to handle complex states and derived data.
- Enhanced lifecycle hooks and debugging tools.
- Better CSS handling and animations.
Now let’s break these down with real-world examples.
Setting Up a Svelte 5 Project
Starting a Svelte 5 project is easier than ever, thanks to the revamped CLI. Run this command to get started:
npx sv create my-svelte-5-app
cd my-svelte-5-app
npm install
npm run dev
You’re now ready to explore the world of Svelte 5!
It's worth noting that the recommended way to setup a new project is by using SvelteKit, the official application framework from the Svelte team powered by Vite If you're completely new to Svelte world, don't worry, you can ignore all the nice features SvelteKit brings and dive into that later.
Building Your First Svelte Component
Svelte components are self-contained files with HTML, CSS, and JavaScript.
Componentes are the building blocks of any Svelte application (and to be honest, of any web application in general). You create components inside .svelte files, that is basically
a superset of HTML.
Here’s a quick example:
<script> let name = 'World'; </script>
<style> h1 { color: #ff3e00; } </style>
<h1>Hello, {name}!</h1>
This component is simple, yet powerful:
- The code between the
<script>block contains Javascript/Typescript that runs when a component instance is created. Variables declared here can be then used in the component's markup. - The
{name}in the markup dynamically updates whenever thenamevariable changes. - Styles are scoped, so the
h1styling won’t affect other components.
Introducing Runes: Reactivity Redefined
The most exciting addition to Svelte 5 is runes, which give you fine-grained control over state management. In previous versions, Svelte’s reactivity was implicit. With runes, it’s now explicit, making your code easier to reason about.
Runes are sumbols that can be used in your .svelte, .svelte.js/ts files to control the Svelte compiler. You can thin on Svelte as it's own language, here runes are part of the
syntax for this language.
Runes are prefixed by the $ symbol and looks like functions, but there are some differences between them and common Javascript functions:
- You don't need to import them since they are "part of the language".
- They can't be used as values or arguments.
- They are only valid at certain positions in the file.
$state for Reactive Variables
Use $state to define a reactive variable:
<script> let count = $state(0); </script>
<button on:click={() => count++}> Clicked {count} times </button>
Whenever count changes, the UI updates automatically.
This rune, allows you to create reactive state, meaning that your UI will react to the changes on the value of that state.
$derived for Computed Values
Use $derived to create reactive values based on other state variables:
<script> let count = $state(0); let doubleCount = $derived(() => count * 2); </script>
<p>Count: {count}</p> <p>Double: {doubleCount}</p> <button on:click={() => count++}>Increment</button>
Here, doubleCount updates whenever count changes, ensuring consistent data flow.
The expression used inside the
$derived(...)rune should be free of side-effects
There are some special cases when you need to create complex derivations of data that don't fit inside the simple and short expression like in the example above. In these cases,
you can use the $derived.by rune that accepts a function as its argument.
<script>
let numbers = $state([1, 2, 3]);
let total = $derived.by(() => {
let total = 0;
for (const n of numbers) {
total += n;
}
return total;
});
</script>
<button onclick={() => numbers.push(numbers.length + 1)}>
{numbers.join(' + ')} = {total}
</button>
Handling Props and Events
Passing data between components is a breeze with props. In Svelte 5, you can use $props to declare props explicitly.
Parent Component
<script> import Child from './Child.svelte'; </script>
<Child message="Hello from parent!" />
Child Component
<script>
let { message } = $props();
</script>
<p>{message}</p>
For events, Svelte allows simple event propagation:
<script> let count = $state(0);
function handleIncrement() { count++; } </script>
<Child on:increment={handleIncrement} />
Child Component
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function increment() { dispatch('increment'); }
</script>
<button on:click={increment}>Increment</button>
Lifecycle Hooks and $effect
Svelte 5 enhances lifecycle hooks and introduces $effect for managing side effects.
Effects are what enable your application to do "stuff". You can consider an effect to any function that perform some action changing the state of the component or the UI itself,. When Svelte runs an effect, it will track what pieces of the state are used by it, and will re-run the function when that state changes.
The $effect run is particularly useful when you need to synchronize an external system or state with the state inside your Svelte app.
The effects run after the component has been mounted to the DOM.
<script> let count = $state(0);
$effect(() => { console.log(`Count changed to ${count}`); });
</script>
<button on:click={() => count++}>Increment</button>
Lifecycle hooks like onMount and onDestroy remain useful for managing component behavior:
<script> import { onMount, onDestroy } from 'svelte';
onMount(() => { console.log('Component mounted'); });
onDestroy(() => { console.log('Component destroyed'); }); </script>
$effects dependencies
This new rune will automatiicaly track any reactive value ($state, $derived, $props) that is used by the function defined inside of it and will register all of them as
dependencies, meaning that when those dependencies change, the $effect will run again.
You should consider
$effectas an escape hatch. It can be very powerful and useful for things like analytics and DOM manipulation. If you need to synchronize states inside the Svelte application instead of$derived.
Snippets: Modular Components Made Easy
Snippets, new in Svelte 5, are inline reusable components that simplify your codebase. They are a way to create reusable pieces of markup.
Let's imagine that for some reason you need to render multiple Counter components, you can, create a snippet for it, and then use the #each tag
{#snippet counter}
<p>Count: {count}</p> <button on:click={() => count++}>Increment</button>
{/snippet}
{#each counters as counter}
{@render counter()}
{/each}
These snippets can include logic, making them perfect for small, reusable UI pieces.
Debugging and Inspect
Svelte 5 introduces enhanced debugging tools like $inspect run, which allows you to monitor component state and props in real-time. This feature makes it easier to identify and
resolve issues during development.
$inspectwill only work on development mode.
This rune is almost the same as console.log, with the exception that it will re-run every time the argument passed to it changes. You can also use a callback that allow you
fine-grained control over what to do when $inpect runs:
<script>
let count = $state(0);
$inspect(count).with((type, count) => {
if (type === 'update') {
debugger; // or `console.trace`, or whatever you want
}
});
</script>
<button onclick={() => count++}>Increment</button>
Conclusion
Svelte 5 takes everything developers love about Svelte and makes it even better. From explicit reactivity with runes to modular snippets and improved debugging tools, Svelte 5 is packed with features that simplify your workflow and enhance performance.
Whether you’re building a small project or a complex web application, Svelte 5 offers the tools and flexibility you need to succeed. So why wait? Dive into Svelte 5 today and experience the future of web development!