DevTools

Cheatsheet Svelte

Framework frontend compilado, leve e ultra performático

Voltar às linguagens
Svelte
75 cards encontrados
Categorias:
Versões:

Setup e Estrutura


8 cards
Criar projecto (SvelteKit)
npm create svelte@latest meu-app
cd meu-app
npm install
npm run dev

create svelte gera um projecto SvelteKit completo com routing, SSR e adaptadores. npm run dev inicia o servidor de desenvolvimento com hot reload em localhost:5173.

Estrutura SvelteKit
src/
  routes/        # páginas e API
  lib/           # código partilhado
  app.html       # template HTML
static/          # ficheiros estáticos
svelte.config.js # configuração

src/routes/ define páginas por ficheiros. src/lib/ guarda componentes e utils reutilizáveis. svelte.config.js configura adaptadores, preprocessores e aliases.

Só Svelte (Vite)
npm create vite@latest meu-app \
  -- --template svelte
cd meu-app
npm install
npm run dev

Template Vite sem SvelteKit — apenas Svelte puro. Ideal para SPAs simples, widgets ou bibliotecas. Sem routing nem SSR incluídos.

svelte.config.js
import adapter from "@sveltejs/adapter-auto";
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";

export default {
  preprocess: vitePreprocess(),
  kit: {
    adapter: adapter(),
    alias: { "@": "./src" }
  }
};

preprocess permite usar TypeScript, SCSS, etc. adapter define o target de deploy (Node, Vercel, static). alias cria atalhos de import como @/lib.

Estrutura SFC
<script>
  // lógica JavaScript
  let nome = "Mundo";
</script>

<!-- template HTML -->
<h1>Olá, {nome}!</h1>

<style>
  /* estilos com scope automático */
  h1 { color: tomato; }
</style>

Um SFC (Single File Component) tem 3 blocos: <script> (lógica), HTML (template) e <style> (CSS com scope). O CSS só afecta o próprio componente.

TypeScript
<script lang="ts">
  let contador: number = 0;
  let nome: string = "Ana";

  interface User {
    nome: string;
    idade: number;
  }
  let user: User = { nome: "Ana", idade: 30 };
</script>

lang="ts" activa TypeScript no script. Funciona com vitePreprocess() no config. Tipos, interfaces e generics são suportados directamente no SFC.

main.js (montagem)
import App from "./App.svelte";

const app = new App({
  target: document.body,
});

export default app;

O ficheiro de entrada instancia o componente raiz com new App(). target define o elemento DOM onde a app é montada. Em SvelteKit, isto é gerado automaticamente.

Estilos globais
<style>
  /* só este componente */
  p { color: blue; }

  /* global (afecta filhos) */
  :global(.card) { padding: 1rem; }

  /* escopo parcial */
  div :global(span) { font-weight: bold; }
</style>

Por defeito, o CSS tem scope ao componente. :global() remove o scope para selectores específicos. Útil para estilizar conteúdo de slots ou bibliotecas externas.

Reatividade


9 cards
Variáveis reativas
<script>
  let contador = 0;

  function incrementar() {
    contador += 1;
  }
</script>

<button on:click={incrementar}>
  {contador}
</button>

Em Svelte, qualquer let no <script> é reactivo. Basta reatribuir (contador += 1) para o DOM actualizar automaticamente. Não precisa de setState ou setters.

$state (Svelte 5)
<script>
  let contador = $state(0);
  let user = $state({ nome: "Ana", idade: 30 });

  // mutação directa funciona:
  user.idade = 31;
  contador++;
</script>

$state() é a nova rune de reactividade do Svelte 5. Cria estado profundamente reactivo — mutações em propriedades aninhadas disparam actualizações sem reatribuição.

$bindable (Svelte 5)
<!-- Filho.svelte -->
<script>
  let { valor = $bindable() } = $props();
</script>
<input bind:value={valor} />

<!-- Pai.svelte -->
<Filho bind:valor={nome} />

$bindable() marca uma prop como vinculável (two-way). O pai usa bind:valor para sincronizar. Substitui o padrão antigo de dispatch + export let.

Declarações reativas ($:)
let preco = 10;
let qtd = 2;

$: total = preco * qtd;
$: console.log("Mudou:", total);
$: if (total > 100) {
  alert("Caro!");
}

$: cria declarações reactivas que re-executam quando as dependências mudam. Funciona como valor derivado, efeito colateral ou bloco condicional reactivo.

$derived (Svelte 5)
<script>
  let preco = $state(10);
  let qtd = $state(2);

  let total = $derived(preco * qtd);
  let comIva = $derived.by(() => {
    return total * 1.23;
  });
</script>

$derived() substitui $: para valores computados. $derived.by() aceita uma função para lógica complexa. Recalcula automaticamente quando dependências mudam.

Arrays e imutabilidade
let itens = [1, 2, 3];

// reactividade requer reatribuição:
itens = [...itens, 4];
itens = itens.filter(n => n > 1);

// objectos:
let user = { nome: "Ana" };
user = { ...user, idade: 30 };

Svelte detecta reatribuição, não mutação profunda. push() sozinho não actualiza — use spread ([...arr, x]) e reatribua. Em Svelte 5 com $state, mutação directa funciona.

$effect (Svelte 5)
<script>
  let contador = $state(0);

  $effect(() => {
    console.log("Contador:", contador);
    return () => {
      console.log("cleanup");
    };
  });
</script>

$effect() substitui $: para efeitos colaterais. Corre após o DOM actualizar. O retorno (função de cleanup) executa antes da próxima execução ou ao destruir.

Bloco reactivo
$: {
  console.log(a, b);
  resultado = a + b;
  if (resultado > 10) {
    mensagem = "Grande";
  }
}

Um bloco $: { } agrupa múltiplas instruções reactivas. Re-executa quando qualquer dependência (a, b) muda. Útil para lógica complexa derivada.

$props (Svelte 5)
<script>
  let { titulo, cor = "azul", ...resto } = $props();
</script>

<h2 style="color: {cor}">{titulo}</h2>

$props() substitui export let para receber props. Usa desestruturação com defaults. ...resto captura props extras (spread). Mais explícito e type-safe.

Template e Binding


9 cards
Interpolação
<p>{nome}</p>
<p>{1 + 1}</p>
<p>{ativo ? "Sim" : "Não"}</p>
<p>{itens.length} itens</p>

Chavetas {} inserem expressões JavaScript no template. Aceitam qualquer expressão válida: variáveis, operações, ternários, chamadas de função. O output é escapado automaticamente.

Two-way binding
<input bind:value={nome} />
<input type="checkbox" bind:checked={ativo} />
<select bind:value={pais}>
  <option>PT</option>
  <option>BR</option>
</select>
<input type="range" bind:value={volume} />

bind:value sincroniza input com variável nos dois sentidos. Funciona com text, checkbox, select, range. Elimina handlers on:input manuais.

Classes e estilos
<div class:ativo={estaAtivo}>
<div class:visivel>

<div style:color={cor} style:font-size="14px">

<!-- Svelte 5: -->
<div class="base" class:ativo={flag}>

class:nome={condição} adiciona/remove classes dinamicamente. style:prop={valor} define estilos inline reactivos. Mais limpo que interpolar strings de class.

If / else
{#if ativo}
  <p>Visível</p>
{:else if outro}
  <p>Outro estado</p>
{:else}
  <p>Oculto</p>
{/if}

Blocos {#if} / {:else if} / {:else} / {/if} fazem renderização condicional. O conteúdo é removido do DOM (não apenas escondido) quando falso.

Binding de elementos
<div bind:clientWidth={w} bind:clientHeight={h}>
  {w}x{h}
</div>

<input bind:this={inputEl} />
<script>
  import { onMount } from "svelte";
  onMount(() => inputEl.focus());
</script>

bind:clientWidth / clientHeight ligam dimensões do elemento a variáveis. bind:this guarda a referência DOM directa para chamar métodos como focus().

Each (listas)
{#each itens as item, i}
  <li>{i}: {item.nome}</li>
{:else}
  <p>Lista vazia</p>
{/each}

{#each itens as item (item.id)}
  <li>{item.nome}</li>
{/each}

{#each} itera arrays. O segundo parâmetro é o índice. {:else} mostra fallback se vazio. A key entre parênteses (item.id) optimiza reordenações.

Binding de grupos
let sabores = [];
{#each ["baunilha", "chocolate", "morango"] as sabor}
  <label>
    <input type="checkbox" bind:group={sabores} value={sabor} />
    {sabor}
  </label>
{/each}
<p>Escolhidos: {sabores.join(", ")}</p>

bind:group sincroniza um array (checkboxes) ou valor (radios) automaticamente. Cada input com o mesmo group partilha a variável. Sem handlers manuais.

Await
{#await fetch("/api/dados")}
  <p>Carregando...</p>
{:then resposta}
  <p>{resposta.ok}</p>
{:catch erro}
  <p>Erro: {erro.message}</p>
{/await}

{#await} renderiza estados de uma Promise: loading, sucesso (:then) e erro (:catch). Elimina necessidade de variáveis loading/error manuais.

HTML raw e @const
{@html conteudoHtml}

{#each itens as item}
  {@const upper = item.nome.toUpperCase()}
  <p>{upper}</p>
{/each}

{@html} insere HTML sem escape (risco de XSS — sanitizar antes!). {@const} declara variáveis locais dentro de blocos {#each} ou {#if}.

Eventos e Ações


8 cards
on:click e handlers
<button on:click={handler}>Função</button>
<button on:click={() => n++}>Inline</button>
<button on:click={fn1} on:click={fn2}>
  Múltiplos
</button>

on:click ouve eventos do DOM. Aceita referência a função ou arrow function inline. Múltiplos handlers no mesmo evento são permitidos e executam por ordem.

Ouvir eventos do filho
<Filho on:salvo={aoSalvar} />

<script>
  function aoSalvar(e) {
    console.log(e.detail.id);
  }
</script>

O pai usa on:nomeEvento para ouvir eventos dispatchados pelo filho. O payload está em e.detail. Comunicação unidireccional: filho emite, pai reage.

Modificadores de evento
<button on:click|once={fn}>Uma vez</button>
<form on:submit|preventDefault={fn}>
<div on:click|stopPropagation={fn}>
<input on:keydown|self={fn}>

Modificadores com |: once (remove após 1º), preventDefault, stopPropagation, self (só se target é o próprio), capture, passive.

Event forwarding
<!-- Wrapper.svelte -->
<button on:click>
  <slot />
</button>

<!-- Uso -->
<Wrapper on:click={handler}>
  Clicar aqui
</Wrapper>

on:click sem handler reencaminha o evento para o pai. O componente wrapper torna-se transparente. Útil para componentes de UI que embrulham elementos nativos.

Objecto do evento
<input on:input={(e) => {
  valor = e.target.value;
}} />

<div on:keydown={(e) => {
  if (e.key === "Enter") enviar();
}}>

O parâmetro e é o evento nativo do DOM. e.target.value acede ao valor do input. e.key identifica a tecla. Funciona com qualquer evento (click, keydown, scroll).

Actions (use:)
<input use:focar />

<script>
  function focar(node) {
    node.focus();
    return {
      update(params) { },
      destroy() { }
    };
  }
</script>

use:acção executa uma função quando o elemento é criado no DOM. Recebe o node e pode retornar update (params mudam) e destroy (cleanup). Ideal para tooltips, focus, drag.

createEventDispatcher
<script>
  import { createEventDispatcher } from "svelte";
  const dispatch = createEventDispatcher();

  function salvar() {
    dispatch("salvo", { id: 1 });
  }
</script>

createEventDispatcher() cria um emissor de eventos personalizados. dispatch("nome", dados) envia para o pai. O pai ouve com on:salvo={handler}. Dados ficam em e.detail.

Actions com parâmetros
<div use:tooltip={{ texto: "Ajuda", pos: "top" }}>
  Info
</div>

<script>
  function tooltip(node, { texto, pos }) {
    // criar tooltip com texto e pos
    return {
      update({ texto, pos }) { },
      destroy() { /* remover */ }
    };
  }
</script>

use:acção={params} passa um objecto como segundo argumento. update() é chamado quando os params mudam reactivamente. Permite acções configuráveis e reutilizáveis.

Componentes


9 cards
Props (export let)
<!-- Cartao.svelte -->
<script>
  export let titulo;
  export let cor = "azul";
</script>

<h2 style="color: {cor}">{titulo}</h2>

export let declara uma prop recebida do pai. O valor por defeito (= "azul") torna-a opcional. Sem default, a prop é obrigatória (warning se não passada).

Named slots
<!-- Cartao.svelte -->
<slot name="cabecalho" />
<slot />
<slot name="rodape" />

<!-- uso -->
<Cartao>
  <svelte:fragment slot="cabecalho">
    Título
  </svelte:fragment>
  Corpo
</Cartao>

slot name="x" cria slots nomeados. O pai usa svelte:fragment slot="x" para preencher. O slot sem nome é o default. Permite layouts complexos com múltiplas zonas.

svelte:self e svelte:element
<!-- recursão -->
{#if profundidade > 0}
  <svelte:self profundidade={profundidade - 1} />
{/if}

<!-- elemento dinâmico -->
<svelte:element this={tag}>
  Conteúdo
</svelte:element>

svelte:self permite recursão (árvores, menus aninhados). svelte:element renderiza um tag HTML dinâmico (h1, p, div) definido por variável.

Usar componente
<script>
  import Cartao from "./Cartao.svelte";
</script>

<Cartao titulo="Olá" cor="red" />
<Cartao titulo="Mundo" />

Importe o componente e use como tag HTML. Props são passadas como atributos. O nome da tag deve ser PascalCase (distingue de elementos HTML nativos). O import pode ser de caminho relativo ou alias $lib.

Slot props
<!-- Lista.svelte -->
{#each itens as item}
  <slot {item} indice={i} />
{/each}

<!-- uso -->
<Lista let:item let:indice>
  <p>{indice}: {item.nome}</p>
</Lista>

Props no <slot> expõem dados do filho ao pai. O pai acede com let:prop. Padrão "render props" — o filho controla os dados, o pai controla a renderização.

Spread de props
<script>
  const dados = { titulo: "Olá", cor: "red" };
</script>

<Cartao {...dados} />
<Cartao {...dados} cor="blue" />

{...objecto} espalha todas as propriedades como props. Props explícitas depois do spread sobrepõem. Útil para passar muitos dados ou repassar props desconhecidas.

setContext / getContext
// pai
import { setContext } from "svelte";
setContext("tema", "escuro");

// filho (qualquer profundidade)
import { getContext } from "svelte";
const tema = getContext("tema");

setContext / getContext partilham dados sem prop drilling. O contexto é definido no pai e acessível em qualquer descendente. Ideal para temas, configs, stores de feature.

Slots
<!-- Cartao.svelte -->
<div class="cartao">
  <slot>Conteúdo padrão</slot>
</div>

<!-- uso -->
<Cartao>Meu texto custom</Cartao>
<Cartao />

<slot> define onde o conteúdo do pai é projectado. O conteúdo entre as tags é o fallback se nada for passado. Permite composição flexível sem props.

svelte:component
<svelte:component this={componenteAtual} />

<script>
  import A from "./A.svelte";
  import B from "./B.svelte";
  let componenteAtual = A;
</script>

svelte:component renderiza dinamicamente o componente em this. Quando a variável muda, o componente antigo é destruído e o novo montado. Útil para tabs, wizards.

Ciclo de Vida e Transições


8 cards
onMount
import { onMount } from "svelte";

onMount(() => {
  console.log("Montado no DOM!");
  const timer = setInterval(tick, 1000);

  return () => {
    clearInterval(timer);
  };
});

onMount corre após o componente ser inserido no DOM. O retorno (função) é o cleanup executado ao destruir. Ideal para timers, subscriptions, fetch de dados.

Transições personalizadas
function typewriter(node, { speed = 50 }) {
  const texto = node.textContent;
  return {
    duration: texto.length * speed,
    tick: (t) => {
      node.textContent = texto.slice(0,
        Math.floor(t * texto.length));
    }
  };
}

Uma transição custom retorna duration e tick(t) onde t vai de 0 a 1. tick é chamado a cada frame com o progresso. Permite animações totalmente personalizadas.

beforeUpdate / afterUpdate
import { beforeUpdate, afterUpdate } from "svelte";

beforeUpdate(() => {
  // antes de qualquer update do DOM
});

afterUpdate(() => {
  // depois do DOM actualizado
});

beforeUpdate corre antes de cada re-render. afterUpdate corre depois do DOM ser actualizado. Úteis para sincronizar bibliotecas externas ou medir elementos.

Animate (flip)
import { flip } from "svelte/animate";

{#each itens as item (item.id)}
  <div animate:flip={{ duration: 300 }}>
    {item.nome}
  </div>
{/each}

animate:flip anima reordenações de listas (First-Last-Invert-Play). Requer key no {#each}. Quando itens mudam de posição, a transição é suave.

onDestroy e tick
import { onDestroy, tick } from "svelte";

onDestroy(() => {
  console.log("Componente removido");
});

// esperar update do DOM:
valor = "novo";
await tick();
// DOM já reflecte "novo"

onDestroy corre quando o componente é removido. tick() retorna uma Promise que resolve após o próximo update do DOM — útil para aceder a valores actualizados.

Key block
{#key valor}
  <div transition:fade>
    Conteúdo para {valor}
  </div>
{/key}

{#key expressão} destrói e recria o conteúdo quando a expressão muda. Útil para reiniciar animações ou componentes sem svelte:component. Combina bem com transições.

Transições (fade, fly, slide)
import { fade, fly, slide } from "svelte/transition";

{#if visivel}
  <div transition:fade={{ duration: 300 }}>
    Olá
  </div>
  <div in:fly={{ y: 200 }} out:slide>
    Animado
  </div>
{/if}

transition: anima entrada e saída. in: / out: separam animações. Built-ins: fade, fly, slide, scale, blur. Aceitam duration, delay, easing.

Crossfade
import { crossfade } from "svelte/transition";
const [send, receive] = crossfade({
  duration: 300
});

<!-- elemento A -->
<div out:send={{ key: id }} />

<!-- elemento B -->
<div in:receive={{ key: id }} />

crossfade cria pares send/receive para animar elementos entre posições (ex: lista → detalhe). A mesma key liga os dois elementos na transição.

Stores


8 cards
Writable store
// stores.js
import { writable } from "svelte/store";
export const contador = writable(0);

// componente
contador.set(5);
contador.update(n => n + 1);

writable(valor) cria um store mutável. set() substitui o valor. update() recebe o valor actual e retorna o novo. Qualquer componente pode ler/escrever.

Store personalizado
function criarContador(inicial) {
  const { subscribe, set, update } = writable(inicial);
  return {
    subscribe,
    incrementar: () => update(n => n + 1),
    decrementar: () => update(n => n - 1),
    reset: () => set(inicial),
  };
}

Exponha apenas subscribe + métodos customizados. Encapsula a lógica e impede set() directo. O consumidor usa $store normalmente. Padrão para stores com regras de negócio.

Auto-subscrição ($)
<script>
  import { contador } from "./stores";
</script>

<p>{$contador}</p>
<button on:click={() => $contador++}>
  Incrementar
</button>

O prefixo $ subscreve automaticamente ao store. {$contador} no template actualiza em tempo real. $contador++ funciona como atalho para update. A unsubscrição é automática.

Subscribe manual
import { contador } from "./stores";

const unsub = contador.subscribe(valor => {
  console.log("Novo:", valor);
});

// quando não precisar mais:
unsub();

subscribe(callback) regista um listener e retorna a função de unsubscribe. Fora de componentes Svelte (JS puro), não há auto-subscrição $ — use este método manualmente.

Readable store
import { readable } from "svelte/store";

export const hora = readable(new Date(), (set) => {
  const id = setInterval(
    () => set(new Date()), 1000
  );
  return () => clearInterval(id);
});

readable() cria um store só de leitura. A função start recebe set para actualizar. O retorno é o cleanup (executa quando não há subscritores). Ideal para relógios, sensores, WebSocket.

Store com localStorage
import { writable } from "svelte/store";

function persisted(key, inicial) {
  const stored = localStorage.getItem(key);
  const store = writable(stored ? JSON.parse(stored) : inicial);
  store.subscribe(v =>
    localStorage.setItem(key, JSON.stringify(v))
  );
  return store;
}

Combine writable com localStorage para persistência. O store hidrata do storage ao criar e grava a cada mudança. Padrão comum para preferências do utilizador.

Derived store
import { derived } from "svelte/store";
import { contador } from "./stores";

export const dobro = derived(
  contador,
  ($contador) => $contador * 2
);

// múltiplos stores:
export const soma = derived(
  [a, b], ([$a, $b]) => $a + $b
);

derived() cria um store calculado a partir de outros. Recalcula automaticamente quando a fonte muda. Aceita um ou múltiplos stores como dependências.

get() (fora de componentes)
import { get } from "svelte/store";
import { contador } from "./stores";

const valor = get(contador);
console.log(valor); // valor actual

get(store) lê o valor actual sem subscrever. Útil em funções utilitárias, testes ou código fora de componentes. Não é reactivo — apenas uma leitura pontual.

SvelteKit


9 cards
Routing por ficheiros
src/routes/
  +page.svelte        // /
  about/
    +page.svelte      // /about
  blog/[slug]/
    +page.svelte      // /blog/:slug

Cada pasta em src/routes/ é uma rota. +page.svelte é a página. [slug] é um parâmetro dinâmico. A estrutura de pastas define os URLs automaticamente.

Layout
<!-- +layout.svelte -->
<nav>Menu global</nav>
<slot />
<footer>Rodapé</footer>

<!-- +layout.js -->
export const prerender = true;

+layout.svelte embrulha todas as páginas da pasta (e sub-pastas). O <slot /> é onde a página renderiza. Layouts aninham hierarquicamente. prerender gera HTML estático.

Adaptadores e deploy
// svelte.config.js
import adapter from "@sveltejs/adapter-node";
// ou: adapter-auto, adapter-static,
//     adapter-vercel, adapter-cloudflare

export default {
  kit: { adapter: adapter() }
};

// build:
npm run build

O adapter define o target de deploy. adapter-node (servidor Node), adapter-static (site estático), adapter-vercel/adapter-cloudflare (edge). npm run build gera o output.

Load function
// +page.js
export async function load({ params, fetch }) {
  const res = await fetch(`/api/posts/${params.slug}`);
  return { post: await res.json() };
}

// +page.svelte
<script>
  export let data;
</script>
<h1>{data.post.titulo}</h1>

load() em +page.js carrega dados antes de renderizar. Corre no cliente e servidor. O retorno fica disponível em data. params dá acesso aos parâmetros da rota.

API endpoints
// +server.js
export async function GET({ params }) {
  return new Response(
    JSON.stringify({ ok: true }),
    { headers: { "Content-Type": "application/json" } }
  );
}

export async function POST({ request }) {
  const body = await request.json();
  return new Response(null, { status: 201 });
}

+server.js cria endpoints HTTP. Exporte funções com nomes de verbos: GET, POST, PUT, DELETE. Retorna Response nativa. Ideal para APIs REST.

Server load
// +page.server.js
export async function load({ params }) {
  const post = await db.posts.find(params.slug);
  return { post };
}

+page.server.js corre apenas no servidor. Ideal para aceder a BD, segredos ou APIs privadas. Os dados são serializados e enviados ao cliente. Nunca expõe código sensível.

Hooks
// hooks.server.js
export async function handle({ event, resolve }) {
  const token = event.cookies.get("token");
  event.locals.user = await getUser(token);
  return resolve(event);
}

// em +page.server.js:
export function load({ locals }) {
  return { user: locals.user };
}

hooks.server.js intercepta todos os requests. handle corre antes de cada rota. event.locals partilha dados entre hooks e load. Ideal para auth global, logging, CORS.

Form actions
// +page.server.js
export const actions = {
  default: async ({ request }) => {
    const data = await request.formData();
    const nome = data.get("nome");
    await db.save({ nome });
    return { sucesso: true };
  }
};

actions processam submissões de formulários no servidor. request.formData() lê os campos. O retorno fica em form na página. Suporta progressive enhancement (funciona sem JS).

Navegação programática
<script>
  import { goto, invalidateAll } from "$app/navigation";
  import { page } from "$app/stores";

  async function logout() {
    await fetch("/logout", { method: "POST" });
    invalidateAll();
    goto("/login");
  }
</script>
<p>Rota actual: {$page.url.pathname}</p>

goto() navega programaticamente. invalidateAll() re-executa todos os load(). $page é um store com URL, params e status. Substitui window.location.

Avançado


7 cards
svelte:window e svelte:body
<svelte:window on:scroll={aoScroll}
  bind:scrollY={y} />
<svelte:body on:mouseenter={fn} />
<svelte:head>
  <title>{titulo} | App</title>
</svelte:head>

svelte:window ouve eventos globais sem addEventListener manual. bind:scrollY sincroniza scroll. svelte:head injecta tags no <head> (SEO, meta tags).

Streaming e defer
// +page.server.js
export function load() {
  return {
    rapido: { titulo: "Olá" },
    lento: db.query("SELECT ...")  // Promise
  };
}

// +page.svelte
{#await data.lento}
  <p>Carregando dados lentos...</p>
{:then resultado}
  <p>{resultado.length} registos</p>
{/await}

Retornar Promises (sem await) no load faz streaming — a página renderiza imediatamente com dados rápidos. {#await} no template mostra loading até resolver.

Snippets (Svelte 5)
{#snippet cartao(item)}
  <div class="card">
    <h3>{item.titulo}</h3>
    <p>{item.texto}</p>
  </div>
{/snippet}

{#each itens as item}
  {@render cartao(item)}
{/each}

{#snippet} define blocos de template reutilizáveis (substitui slot props). {@render} invoca o snippet com argumentos. Mais explícito e composável que slots nomeados.

Testing (Vitest)
import { render, screen } from "@testing-library/svelte";
import { test, expect } from "vitest";
import Cartao from "./Cartao.svelte";

test("mostra titulo", () => {
  render(Cartao, { props: { titulo: "Olá" } });
  expect(screen.getByText("Olá")).toBeTruthy();
});

@testing-library/svelte renderiza componentes em testes. render(Componente, { props }) monta no DOM virtual. screen.getByText procura elementos. Corre com vitest.

Acessibilidade (a11y)
<!-- Svelte warns at compile time: -->
<img src="foto.jpg" alt="Descrição" />
<button aria-label="Fechar">×</button>
<input aria-invalid={!!erro} />

<!-- svelte-ignore para suprimir -->
<!-- svelte-ignore a11y-missing-content -->

Svelte emite warnings de acessibilidade em compilação: alt em imagens, labels em inputs, roles. svelte-ignore suprime warnings específicos quando necessário.

Performance e boas práticas
<!-- keyed each para listas grandes -->
{#each itens as item (item.id)}
  <Item {item} />
{/each}

<!-- lazy load de componentes -->
<script>
  import { onMount } from "svelte";
  let Pesado;
  onMount(async () => {
    Pesado = (await import("./Pesado.svelte")).default;
  });
</script>

Use key em {#each} para diffs eficientes. import() dinâmico faz code-splitting. Svelte compila para vanilla JS — sem virtual DOM, actualizações cirúrgicas no DOM real.

Error boundaries
<!-- +error.svelte -->
<script>
  import { page } from "$app/stores";
</script>

<h1>{$page.status}: {$page.error.message}</h1>
<a href="/">Voltar ao início</a>

+error.svelte renderiza quando uma rota lança erro. $page.status e $page.error dão detalhes. Pode existir por rota ou global em src/routes/.