Sintassi Base
Struttura di un Componente .svelte
Ogni componente Svelte è un file con estensione .svelte composto da tre sezioni opzionali: script, markup (template) e style.
<script>
// Logica JavaScript/TypeScript
let messaggio = $state('Ciao, Svelte!');
</script>
<!-- Markup HTML (template) -->
<h1>{messaggio}</h1>
<style>
/* CSS con scope automatico */
h1 {
color: #ff3e00;
font-size: 2rem;
}
</style>
L’ordine delle sezioni non è obbligatorio, ma la convenzione è: <script>, markup, <style>.
La sezione script
La sezione <script> contiene la logica del componente. Qui si dichiarano variabili, si importano moduli e si definiscono funzioni.
<script>
import { onMount } from 'svelte';
import Header from '$lib/components/Header.svelte';
// Variabili di stato (Svelte 5)
let contatore = $state(0);
let nome = $state('');
// Variabili normali (non reattive)
const API_URL = 'https://api.example.com';
// Funzioni
function incrementa() {
contatore++;
}
// Lifecycle
onMount(() => {
console.log('Componente montato');
});
</script>
Il markup (template)
Il markup è puro HTML arricchito con la sintassi di Svelte per espressioni, binding, blocchi logici e direttive.
<header>
<h1>La mia app</h1>
</header>
<main>
<p>Benvenuto, {nome}!</p>
<button onclick={incrementa}>Clicca</button>
</main>
<footer>
<p>© 2026</p>
</footer>
La sezione style
Gli stili sono scoped al componente per default: le regole CSS si applicano solo al markup di quel componente, non ai componenti figli o al resto della pagina.
<style>
/* Questo p è SOLO per i <p> di questo componente */
p {
color: navy;
line-height: 1.6;
}
/* Classi locali */
.evidenziato {
background: yellow;
padding: 0.2rem 0.5rem;
}
</style>
Espressioni nel Markup
Interpolazione di variabili
Usa le parentesi graffe {} per inserire espressioni JavaScript nel markup:
<script>
let nome = $state('Luca');
let eta = $state(28);
let items = $state(['Mela', 'Banana', 'Arancia']);
</script>
<!-- Variabile semplice -->
<p>Ciao, {nome}!</p>
<!-- Espressione calcolata -->
<p>L'anno prossimo avrai {eta + 1} anni</p>
<!-- Metodi su stringhe/array -->
<p>{nome.toUpperCase()}</p>
<p>Hai {items.length} frutti</p>
<!-- Operatore ternario -->
<p>{eta >= 18 ? 'Maggiorenne' : 'Minorenne'}</p>
<!-- Template literal -->
<p>{`${nome} ha ${eta} anni`}</p>
Espressioni in attributi HTML
<script>
let url = $state('https://svelte.dev');
let attivo = $state(true);
let colore = $state('#ff3e00');
</script>
<!-- Attributo con espressione -->
<a href={url}>Link</a>
<!-- Attributo con shorthand (nome variabile = nome attributo) -->
<div id={url}></div>
<!-- Attributo condizionale -->
<button disabled={!attivo}>Invia</button>
<!-- Stile inline dinamico -->
<p style="color: {colore}">Testo colorato</p>
<p style:color={colore}>Alternativa con direttiva style</p>
<!-- Classe condizionale -->
<div class:attivo>Sono attivo?</div>
<div class:evidenziato={eta > 18}>Evidenziato se maggiorenne</div>
Spread di attributi
<script>
let inputProps = $state({
type: 'email',
placeholder: 'La tua email',
required: true
});
</script>
<!-- Tutti gli attributi dall'oggetto -->
<input {...inputProps} />
Variabili Reattive in Svelte 5
In Svelte 5, la reattività è esplicita tramite le runes:
<script>
// $state: crea una variabile reattiva
let contatore = $state(0);
// Oggetti reattivi - la reattività è profonda (deep)
let utente = $state({
nome: 'Mario',
eta: 30,
indirizzo: {
citta: 'Roma'
}
});
// Array reattivi
let lista = $state(['primo', 'secondo']);
function aggiungi() {
// Push funziona direttamente! (in Svelte 4 serviva lista = [...lista, 'nuovo'])
lista.push('nuovo');
}
function aggiornaCitta() {
// Mutazione profonda reattiva
utente.indirizzo.citta = 'Milano';
}
</script>
<p>Contatore: {contatore}</p>
<p>Utente: {utente.nome} da {utente.indirizzo.citta}</p>
<p>Lista: {lista.join(', ')}</p>
Confronto con Svelte 4
<!-- Svelte 4: ogni variabile let è automaticamente reattiva -->
<script>
let contatore = 0;
let lista = ['primo'];
function aggiungi() {
// In Svelte 4 serviva riassegnare per triggerare la reattività
lista = [...lista, 'nuovo'];
// lista.push('nuovo') NON triggerava aggiornamenti in Svelte 4!
}
</script>
<!-- Svelte 5: reattività esplicita con $state -->
<script>
let contatore = $state(0);
let lista = $state(['primo']);
function aggiungi() {
// In Svelte 5 le mutazioni funzionano direttamente
lista.push('nuovo');
}
</script>
HTML Dinamico
Rendering di HTML raw
Quando hai bisogno di inserire HTML non-escaped, usa il tag speciale {@html}:
<script>
let contenutoHTML = $state('<strong>Testo in grassetto</strong> e <em>corsivo</em>');
</script>
<!-- ATTENZIONE: non usare con input dell'utente (rischio XSS) -->
{@html contenutoHTML}
Tag di debug
Per il debug in fase di sviluppo:
<script>
let contatore = $state(0);
let utente = $state({ nome: 'Test' });
</script>
<!-- Logga nel devtools quando i valori cambiano -->
{@debug contatore, utente}
<p>{contatore}</p>
Costanti nel markup
{@const area = larghezza * altezza}
<p>L'area è {area}</p>
Nota: {@const} funziona solo all’interno di blocchi logici come {#each} o {#if}.
CSS Scoped
Come funziona
Svelte aggiunge automaticamente una classe univoca (come .svelte-1a2b3c) a ogni elemento del componente e ai selettori CSS, rendendo gli stili locali:
<!-- Questo componente -->
<p class="messaggio">Ciao</p>
<style>
.messaggio { color: blue; }
</style>
<!-- Viene compilato in qualcosa come: -->
<!-- <p class="messaggio svelte-xyz123">Ciao</p> -->
<!-- .messaggio.svelte-xyz123 { color: blue; } -->
Stili globali con :global()
Per applicare stili che escono dallo scope del componente:
<style>
/* Solo per questo componente */
p { color: blue; }
/* Globale: si applica ovunque */
:global(body) {
margin: 0;
font-family: system-ui;
}
/* Classe globale dentro un selettore locale */
div :global(.highlight) {
background: yellow;
}
</style>
Direttive class: e style:
<script>
let attivo = $state(false);
let colore = $state('red');
let dimensione = $state(16);
</script>
<!-- Classe condizionale -->
<div class:attivo>Toggle</div>
<div class:attivo={contatore > 5}>Condizionale</div>
<!-- Classe con nome diverso dalla variabile -->
<div class:bg-rosso={errore}>Errore</div>
<!-- Stile inline reattivo -->
<p style:color={colore} style:font-size="{dimensione}px">
Testo stilizzato
</p>
<!-- Stile con !important -->
<p style:color|important={colore}>Priorità alta</p>
<style>
.attivo {
font-weight: bold;
color: green;
}
.bg-rosso {
background: red;
color: white;
}
</style>
Componenti nel Markup
Importazione e uso
<script>
import Bottone from '$lib/components/Bottone.svelte';
import Card from '$lib/components/Card.svelte';
</script>
<!-- I componenti iniziano con la lettera maiuscola -->
<Bottone testo="Clicca qui" />
<Card titolo="La mia card">
<p>Contenuto della card</p>
</Card>
Componenti dinamici
<script>
import Rosso from './Rosso.svelte';
import Verde from './Verde.svelte';
import Blu from './Blu.svelte';
const componenti = { rosso: Rosso, verde: Verde, blu: Blu };
let selezione = $state('rosso');
</script>
<!-- svelte:component per rendering dinamico (Svelte 4) -->
<svelte:component this={componenti[selezione]} />
<!-- In Svelte 5, puoi usare direttamente una variabile -->
{@const Componente = componenti[selezione]}
<Componente />
Elementi Speciali di Svelte
<!-- Accesso alla finestra del browser -->
<svelte:window on:keydown={handleKeydown} bind:scrollY={y} />
<!-- Accesso al body -->
<svelte:body on:mouseenter={handleMouseenter} />
<!-- Accesso al document -->
<svelte:document on:visibilitychange={handleVisibility} />
<!-- Head HTML (per meta tag, title, ecc.) -->
<svelte:head>
<title>La mia pagina</title>
<meta name="description" content="Descrizione pagina" />
</svelte:head>
Best Practice
- Mantieni i componenti piccoli: se un componente supera le 100-150 righe, valuta di dividerlo
- Ordine delle sezioni:
<script>, markup,<style>per consistenza - Usa TypeScript:
<script lang="ts">per type safety - Evita
{@html}con input non fidati per prevenire attacchi XSS - Preferisci
class:alle espressioni ternarie per le classi condizionali - Nomina i file con PascalCase:
MioComponente.svelteper distinguerli dagli elementi HTML