Svelte 5: Runes e Nuova Reattività - Guida Completa
Esplora Svelte 5: sistema di reattività con Runes, Snippets per riuso componenti, event handlers migliorati e performance eccezionali.

Svelte 5 rappresenta la più grande evoluzione del framework con l'introduzione delle Runes, un nuovo sistema di reattività più potente, esplicito e performante. Rilasciato in versione stabile, porta miglioramenti significativi a performance, developer experience e composizione dei componenti.
🎯 Novità Principali
Runes: Il Nuovo Sistema di Reattività
Le Runes sostituiscono la reattività implicita con un sistema più esplicito e potente:
$state - Stato Reattivo
<script>
// ❌ Svelte 4 - reattività implicita
let count = 0;
function increment() {
count += 1;
}
// ✅ Svelte 5 - Runes esplicite
let count = $state(0);
function increment() {
count += 1;
}
// Oggetti reattivi profondi
let user = $state({
name: 'Mario',
settings: {
theme: 'dark',
notifications: true
}
});
// Mutazioni profonde sono reattive
function toggleTheme() {
user.settings.theme = user.settings.theme === 'dark' ? 'light' : 'dark';
}
// Array reattivi
let todos = $state([
{ id: 1, text: 'Learn Svelte 5', done: false }
]);
function addTodo(text) {
todos.push({ id: Date.now(), text, done: false });
}
</script>
<button on:click={increment}>
Count: {count}
</button>
<button on:click={toggleTheme}>
Theme: {user.settings.theme}
</button>
$derived - Valori Derivati
<script>
let count = $state(0);
// ❌ Svelte 4 - reactive declarations
$: doubled = count * 2;
$: quadrupled = doubled * 2;
// ✅ Svelte 5 - $derived
let doubled = $derived(count * 2);
let quadrupled = $derived(doubled * 2);
// Con logica complessa
let items = $state([1, 2, 3, 4, 5]);
let evenItems = $derived(items.filter(n => n % 2 === 0));
let sum = $derived(items.reduce((a, b) => a + b, 0));
// Derived con side effects
let searchQuery = $state('');
let searchResults = $derived.by(() => {
if (!searchQuery) return [];
// Computazione pesante
return database.search(searchQuery);
});
</script>
<div>
<p>Count: {count}</p>
<p>Doubled: {doubled}</p>
<p>Quadrupled: {quadrupled}</p>
<p>Even items: {evenItems.join(', ')}</p>
<p>Sum: {sum}</p>
</div>
$effect - Side Effects
<script>
let count = $state(0);
let name = $state('');
// ❌ Svelte 4 - reactive statements
$: {
console.log('Count changed:', count);
document.title = `Count: ${count}`;
}
// ✅ Svelte 5 - $effect
$effect(() => {
console.log('Count changed:', count);
document.title = `Count: ${count}`;
});
// Con cleanup
$effect(() => {
const interval = setInterval(() => {
console.log('Current count:', count);
}, 1000);
// Cleanup automatico
return () => clearInterval(interval);
});
// Pre effect (esegue prima del DOM update)
$effect.pre(() => {
// Leggi valori prima del render
const oldValue = count;
// Codice eseguito prima del DOM update
});
// Effect root per controllo granulare
import { effect } from 'svelte';
const cleanup = effect.root(() => {
$effect(() => {
console.log('Reactive effect');
});
// Restituisce funzione cleanup
});
// Chiama quando necessario
cleanup();
</script>
$props - Props Tipizzate
<!-- Counter.svelte -->
<script>
// ❌ Svelte 4
export let count = 0;
export let onIncrement;
// ✅ Svelte 5 - $props con destructuring
let { count = 0, onIncrement } = $props();
// Con TypeScript
interface Props {
count?: number;
max?: number;
onIncrement?: () => void;
}
let {
count = 0,
max = 100,
onIncrement
}: Props = $props();
// Props reattive
let doubled = $derived(count * 2);
function increment() {
if (count < max) {
onIncrement?.();
}
}
</script>
<button on:click={increment} disabled={count >= max}>
Count: {count} (doubled: {doubled})
</button>
<!-- Usage -->
<script>
let value = $state(0);
</script>
<Counter
count={value}
max={50}
onIncrement={() => value++}
/>
$bindable - Two-way Binding
<!-- TextField.svelte -->
<script>
// Props bindable dall'esterno
let { value = $bindable('') } = $props();
</script>
<input bind:value />
<!-- Parent.svelte -->
<script>
let text = $state('');
</script>
<!-- Two-way binding -->
<TextField bind:value={text} />
<p>You typed: {text}</p>
Snippets: Riuso Template
I Snippets sostituiscono gli slots nominati con un sistema più potente:
<script>
let items = $state([
{ id: 1, name: 'Apple', price: 1.5 },
{ id: 2, name: 'Banana', price: 0.8 }
]);
</script>
<!-- Definizione snippet -->
{#snippet itemCard(item)}
<div class="card">
<h3>{item.name}</h3>
<p>${item.price}</p>
</div>
{/snippet}
<!-- Snippet con parametri multipli -->
{#snippet productDetails(item, index, isLast)}
<div class="product" class:last={isLast}>
<span class="index">#{index + 1}</span>
<h4>{item.name}</h4>
<p class="price">${item.price}</p>
</div>
{/snippet}
<!-- Uso snippet -->
<div class="grid">
{#each items as item, i}
{@render itemCard(item)}
{/each}
</div>
<div class="list">
{#each items as item, i}
{@render productDetails(item, i, i === items.length - 1)}
{/each}
</div>
<!-- Snippet da props -->
<script>
let {
items,
renderItem = $props()
} = $props();
</script>
<div>
{#each items as item}
{@render renderItem(item)}
{/each}
</div>
<!-- Usage -->
<ItemList {items}>
{#snippet renderItem(item)}
<div class="custom-item">{item.name}</div>
{/snippet}
</ItemList>
Event Handlers Modernizzati
<script>
let count = $state(0);
// ❌ Svelte 4 - on: directive
// <button on:click={increment}>
// ✅ Svelte 5 - onclick prop
function increment() {
count++;
}
// Con event object
function handleClick(event) {
console.log('Clicked at', event.clientX, event.clientY);
count++;
}
// Custom events
function handleCustom(detail) {
console.log('Custom event:', detail);
}
</script>
<button onclick={increment}>
Count: {count}
</button>
<button onclick={handleClick}>
Click with event
</button>
<!-- Custom components -->
<CustomButton
onclick={() => console.log('clicked')}
ondblclick={() => console.log('double clicked')}
/>
🚀 Performance Improvements
Reattività Ottimizzata
<script>
// Svelte 5: tracking granulare
let data = $state({
user: { name: 'Mario', age: 30 },
settings: { theme: 'dark' },
posts: []
});
// Solo i componenti che usano data.user.name si ri-renderizzano
let userName = $derived(data.user.name);
// Fine-grained reactivity
$effect(() => {
// Traccia solo data.user.name, non tutto l'oggetto
console.log('Username:', data.user.name);
});
</script>
Bundle Size Ridotto
<!-- Svelte 5 genera codice più compatto -->
<!-- Svelte 4: ~4KB runtime -->
<!-- Svelte 5: ~2.5KB runtime -->
<!-- 40% riduzione bundle size! -->
Compilazione Più Veloce
- 50% più veloce compile time
- Meno codice generato dal compiler
- Tree-shaking migliorato
🎨 Nuove Features
Class Directives Migliorate
<script>
let isActive = $state(false);
let theme = $state('dark');
</script>
<!-- Sintassi più concisa -->
<div class:active={isActive}>Content</div>
<!-- Shorthand quando nome variabile = classe -->
<div class:isActive>Content</div>
<!-- Multiple classes -->
<div
class:active={isActive}
class:dark={theme === 'dark'}
class:light={theme === 'light'}
>
Themed content
</div>
Style Directives
<script>
let color = $state('#ff3e00');
let size = $state(16);
</script>
<!-- Inline styles reattive -->
<div
style:color
style:font-size="{size}px"
style:background-color={color}
>
Styled content
</div>
Transitions Migliorate
<script>
import { fade, fly, slide } from 'svelte/transition';
let visible = $state(false);
</script>
<!-- Transitions più performanti in Svelte 5 -->
{#if visible}
<div
in:fly={{ y: 200, duration: 500 }}
out:fade={{ duration: 200 }}
>
Animated content
</div>
{/if}
🔧 Migration da Svelte 4
Migration Tool Automatico
# Installa npx migration tool
npx sv migrate svelte-5
# Opzioni
npx sv migrate svelte-5 --help
Esempio Migrazione
<!-- Before (Svelte 4) -->
<script>
export let count = 0;
export let max = 100;
$: doubled = count * 2;
$: if (count > max) {
count = max;
}
$: {
console.log('Count:', count);
}
</script>
<button on:click={() => count++}>
{count}
</button>
<!-- After (Svelte 5) -->
<script>
let { count = 0, max = 100 } = $props();
let doubled = $derived(count * 2);
$effect(() => {
if (count > max) {
count = max;
}
});
$effect(() => {
console.log('Count:', count);
});
</script>
<button onclick={() => count++}>
{count}
</button>
🔄 Come Aggiornare a Svelte 5
Installazione
# npm
npm install svelte@latest
# pnpm
pnpm add svelte@latest
# Aggiorna anche SvelteKit se usato
npm install @sveltejs/kit@latest
Setup Vite
// vite.config.js
import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";
export default defineConfig({
plugins: [
svelte({
compilerOptions: {
// Abilita Runes
runes: true,
},
}),
],
});
TypeScript
// tsconfig.json
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"strict": true,
"moduleResolution": "bundler",
"types": ["svelte"]
}
}
⚠️ Breaking Changes
1. Reattività Implicita Deprecata
<!-- ❌ Non più reattivo automaticamente -->
<script>
let count = 0; // Non reattivo!
</script>
<!-- ✅ Usa $state -->
<script>
let count = $state(0); // Reattivo
</script>
2. $ Label Riservato
<!-- ❌ Errore in Svelte 5 -->
<script>
$: doubled = count * 2; // Solo con legacy mode
</script>
<!-- ✅ Usa $derived -->
<script>
let doubled = $derived(count * 2);
</script>
3. on: Directive Deprecata
<!-- ❌ Legacy -->
<button on:click={handler}>Click</button>
<!-- ✅ Nuovo -->
<button onclick={handler}>Click</button>
🎓 Best Practices
1. Usa Runes per Reattività
<script>
// ✅ Sempre
let state = $state(initialValue);
let derived = $derived(computation);
$effect(() => { /* side effects */ });
</script>
2. Snippets per Riuso
<!-- ✅ DRY con snippets -->
{#snippet card(item)}
<div class="card">{item.name}</div>
{/snippet}
{#each items as item}
{@render card(item)}
{/each}
3. $effect con Cleanup
<script>
// ✅ Sempre cleanup
$effect(() => {
const timer = setInterval(() => {}, 1000);
return () => clearInterval(timer);
});
</script>
📊 Confronto Performance
| Metrica | Svelte 4 | Svelte 5 | Miglioramento |
|---|---|---|---|
| Bundle size | 4KB | 2.5KB | -38% |
| Compile time | 200ms | 100ms | 2x |
| Runtime perf | Baseline | 1.3x | +30% |
| Memory usage | 100% | 80% | -20% |
🔗 Risorse Utili
💡 Conclusioni
Svelte 5 è un upgrade rivoluzionario:
✅ Runes per reattività esplicita e potente ✅ Snippets per composizione migliorata ✅ Performance 30% migliori ✅ Bundle size -38% ✅ Developer Experience modernizzata
Quando aggiornare:
- ✅ Nuovi progetti: usa Svelte 5
- ⚠️ Progetti esistenti: migration tool disponibile
- 📚 Graduale: backward compatibility con legacy mode
Svelte 5 mantiene la semplicità che ha reso famoso Svelte, aggiungendo potenza e scalabilità!