Cheatsheet Svelte
Framework frontend compilado, leve e ultra performático
Svelte
Setup e Estrutura
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
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
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
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
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
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
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 actualget(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
Routing por ficheiros
src/routes/
+page.svelte // /
about/
+page.svelte // /about
blog/[slug]/
+page.svelte // /blog/:slugCada 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 buildO 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
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/.