Communitygithub.com

agentmemoir/skills

Construa, migre ou revise aplicativos Svelte 5 renderizados no servidor usando runes ($state, $derived, $effect, $props, $bindable) com SvelteKit SSR (adapter-auto/node/cloudflare/vercel/netlify) ou Vite SSR puro usando svelte/server render() e hydrate().

O que é skills?

Esta skill é especializada em renderização do lado do servidor (SSR) com Svelte 5 e suas runes. Ela cobre tanto aplicativos SvelteKit completos (com adaptadores para Vercel, Netlify, Cloudflare, Node) quanto a integração de componentes Svelte em backends existentes usando Vite SSR puro. O foco principal está nas armadilhas específicas do SSR com runes: $effect nunca executa no servidor, estado no nível do módulo vaza entre requisições de usuários diferentes, e valores não determinísticos (como Date.now() ou Math.random()) causam erros de hidratação. A skill oferece padrões de correção para cada um desses problemas, instruções detalhadas sobre como manter o estado por requisição (usando load functions, context, e event.locals), e uma tabela completa do comportamento de cada rune durante o SSR. Além disso, inclui guias de segurança (CSP, CSRF, SSRF, tratamento de segredos) e um checklist de auditoria para revisar aplicativos existentes. É ideal para desenvolvedores que estão migrando para Svelte 5, construindo apps com requisitos de SEO e primeira pintura rápida, ou depurando bugs de hidratação.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/agentmemoir/skills/tree/HEAD/skills/frontend/svelte5-ssr-runes

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Renderização do Lado do Servidor (Runes) no Svelte 5

O que esta skill cobre

"SSR" significa que cada requisição é renderizada em HTML por um servidor (Node, Bun, serverless ou edge) antes do navegador recebê-la; os mesmos componentes então hidratam em um aplicativo interativo. Diferente de uma build apenas estática, existe um servidor real no momento da requisição, então +page.server.js, +layout.server.js, +server.js, form actions e hooks.server.js são permitidos — e é onde os dados e segredos pertencem.

A parte das runes é o que torna o SSR complicado: cada componente executa duas vezes, uma no servidor e uma no navegador. Código que funciona bem em um app apenas cliente quebra aqui:

  • $effect nunca executa no servidor, então qualquer estado que ele preenche está faltando no HTML do servidor.
  • $state no nível do módulo em arquivos .svelte.ts é criado uma vez por processo do servidor e compartilhado entre todas as requisições dos usuários.
  • Valores calculados a partir de Date.now(), Math.random() ou localStorage durante a renderização diferem entre servidor e cliente, causando incompatibilidades de hidratação.

Essas três falhas são responsáveis pela maioria dos bugs de SSR em código com runes, e cada uma tem um pequeno padrão correto abaixo.

Passo 1 — Escolha a forma do projeto

NecessidadeUse
App completo: rotas, carregamento de dados do servidor, formulários, autenticação, streamingSvelteKit com adapter-auto (Vercel/Netlify/Cloudflare auto-detectam), adapter-node (auto-hospedado) ou um adaptador de plataforma. Este é o padrão; use a menos que haja um motivo para não usar.
Uma árvore de componentes Svelte dentro de um backend existente (Express/Hono/Fastify/Bun)Vite + Svelte 5 puro: render() de svelte/server no servidor, hydrate() no navegador. Você é responsável por roteamento, cabeçalhos e manipulação de requisições.
Conteúdo majoritariamente estático com algumas rotas dinâmicasSvelteKit com export const prerender = true por rota (ou 'auto' para pré-renderizar páginas populares e renderizar no servidor as páginas de cauda longa). As rotas dinâmicas ainda seguem todas as regras aqui.

Não defina export const ssr = false para "corrigir" um bug de SSR — isso transforma a rota em uma casca renderizada no cliente (um SPA), perdendo SEO e a primeira pintura. É legítimo apenas para páginas que genuinamente não podem ser renderizadas sem globais do navegador. csr = false é a opção oposta: HTML do servidor sem hidratação, para páginas que não precisam de interatividade.

As etapas de configuração para ambos os caminhos, além de adaptadores e opções de página, estão em references/ssr-build.md. Leia-o antes de gerar svelte.config.js / vite.config.js, porque os nomes das opções e o comportamento dos adaptadores diferem entre versões e é fácil errar sutilmente.

Passo 2 — Saiba quais runnes executam no servidor

Esta tabela é o núcleo da skill:

RuneDurante SSRNotas
$state(v)ExecutaO valor inicial é renderizado. Mutá-lo durante a renderização não re-renderiza HTML já emitido — SSR é uma única passada.
$state.raw(v)ExecutaSem proxy. Bom para grandes cargas de data que você substitui por completo em vez de mutar.
$derived(expr)ExecutaAvaliado como parte da renderização, então sua saída vai para o HTML. Mantenha puro e livre de APIs exclusivas do navegador.
$effect / $effect.preNuncaApenas no navegador. Estado que ele define está ausente do HTML SSR (aparece após hidratação). Isso não é um erro de hidratação, mas é HTML vazio e mudança de layout visível.
$effect.root(fn)NuncaO mesmo. Não construa estado visível no servidor dentro dele.
$props() / $bindable()ExecutaOs dados carregados chegam como props; os valores iniciais renderizam.
$props.id()ExecutaID único seguro para SSR, consistente entre servidor e cliente (Svelte ≥ 5.20). Use para links for/aria-labelledby.
$inspect(v)Apenas devNenhuma saída SSR.
$state.snapshot(v)ExecutaUse antes de passar estado para uma API não-Svelte ou biblioteca do servidor.
$effect.tracking()falseO compilador do servidor o substitui pelo literal false; não há contexto de rastreamento durante SSR.

O bug clássico de SSR que esta tabela previne:

<!-- ❌ vazio no HTML do servidor, depois aparece após hidratação -->
<script>
  let user = $state();
  $effect(() => { user = getUser(); });
</script>
<div>{user?.name}</div>

A correção é quase sempre a mesma: calcule valores visíveis no SSR em $derived, ou busque-os em load e passe como props. Mantenha $effect para coisas que só existem no navegador (canvas, observadores, analytics, sincronização de armazenamento).

Leia references/runes-ssr.md para o comportamento completo de cada rune no servidor, padrões de valor seguros para hidratação e o status experimental de SSR assíncrono.

Passo 3 — Mantenha o estado por requisição, nunca global do módulo

// ❌ um único objeto compartilhado para todo o processo do servidor — o usuário A pode ver dados do usuário B
// src/lib/user.svelte.ts
export const user = $state({ name: '' });

A correção canônica é retornar dados por requisição de load e passá-los como props; quando muitos componentes aninhados precisam deles, coloque-os em contexto com um getter:

<!-- +layout.svelte -->
<script>
  import { setContext } from 'svelte';
  let { data } = $props();
  setContext('user', () => data.user);
</script>
<!-- qualquer filho -->
<script>
  import { getContext } from 'svelte';
  const user = getContext('user');
</script>
<p>Olá {user().name}</p>

O contexto é anexado à árvore de componentes, e cada requisição SSR tem sua própria árvore, então não pode vazar entre usuários. Passe uma função (ou objeto com getters) para setContext, não o valor em si, ou a reatividade é perdida. Dados por requisição que muitas funções do servidor precisam podem viver em event.locals, preenchidos em hooks.server.ts.

Estado no nível do módulo ainda é aceitável para estado de aplicativo apenas cliente que nenhum dado de usuário toca (tema, feature flags), desde que nada renderize valores personalizados a partir dele no servidor.

Passo 4 — Trate a hidratação como um contrato

A hidratação reproduz o HTML do servidor; a primeira renderização do cliente deve corresponder a ele. Qualquer valor diferente causa um hydration_mismatch, que é deliberadamente não suprimível — corrija o código, não o aviso.

  • Renderize deterministicamente: sem Date.now(), sem formatação com new Date(), sem Math.random(), sem crypto.randomUUID(), sem leituras de localStorage/window/navigator no caminho de renderização.
  • Gere IDs com $props.id(), não com aleatoriedade ou contadores de módulo.
  • Formate datas no servidor em load e passe strings, ou sempre passe um timeZone explícito — os fusos do servidor e cliente diferem.
  • Valores apenas para cliente (armazenamento, media queries, geolocalização): comece com um padrão seguro para o servidor e preencha dentro de $effect/onMount, ou feche a marcação com uma flag mounted e um fallback estável no servidor.
<script>
  import { browser } from '$app/environment';

  let theme = $state('light');
  $effect(() => {
    if (!browser) return;
    theme = localStorage.getItem('theme') ?? 'light'; // após montagem, então a hidratação corresponde
  });
</script>

Em SSR puro do Svelte (sem SvelteKit), $app/environment não está disponível — use typeof window !== 'undefined' dentro de um efeito, nunca no caminho de renderização.

Passo 5 — Carregue e transmita dados no servidor

  • +page.server.ts / +layout.server.ts para bancos de dados, arquivos e env privado; +page.ts universal para APIs públicas que podem executar em qualquer lugar.
  • load do servidor deve retornar dados que sobrevivam à serialização devalue: JSON mais Date, Map, Set, BigInt, RegExp e referências cíclicas. Sem funções ou instâncias de classe.
  • Dados lentos e não essenciais: retorne a promise e use {#await} na página — promises aninhadas transmitem à medida que resolvem. Aguarde dados essenciais antes de retornar para que estejam na primeira pintura.
  • Use event.fetch dentro de load: ele herda cookies da requisição e inclui na página as respostas para hidratação em vez de buscá-las novamente.
  • Após navegação, data é atualizado sem recriar o componente: derive dele ($derived) em vez de copiá-lo para locais não reativos.

SSR assíncrono (await em componentes) e funções remotas ainda são experimentais a partir de Svelte 5.57 / SvelteKit 2.7x (Setembro 2026). Não habilite compilerOptions.experimental.async em código de produção a menos que eu peça explicitamente; o status exato e a opção de ativação estão em references/runes-ssr.md.

Passo 6 — Aplique as regras de segurança do SSR

  1. Segredos permanecem privados. $env/static/private / $env/dynamic/private são apenas do servidor; apenas variáveis prefixadas com PUBLIC_ chegam ao navegador. Importações de $lib/server/ e *.server.js geram erro se um componente as puxar.
  2. Tudo o que é retornado de load está embutido no payload HTML — todo ele, não apenas os campos que você renderiza. Nunca retorne hashes de senha, tokens ou campos internos que você não precisa no cliente.
  3. CSP com nonces. Configure kit.csp; o SvelteKit injeta um nonce em seus próprios scripts inline, e %sveltekit.nonce% cobre tags manuais em app.html. Um servidor real pode enviar cabeçalhos CSP reais — use isso em vez de uma tag <meta> mais fraca.
  4. {@html} ainda é XSS. Renderizado no servidor não significa sanitizado. Sanitize HTML não confiável com DOMPurify imediatamente antes de renderizar.
  5. Autenticação pertence a hooks.server.ts (preencha locals) mais verificações no nível da rota. Funções load de layout não executam em toda navegação do lado do cliente.
  6. Proteção CSRF está ativada por padrão para envios de formulário; deixe-a ativada, e verifique a origem você mesmo em qualquer endpoint de mutação +server.js personalizado.
  7. Cookies de sessão: httpOnly, secure em produção, sameSite: 'lax' no mínimo.
  8. fetch do lado do servidor de URLs fornecidas pelo usuário é SSRF. Faça allowlist de hosts; bloqueie IPs privados e endpoints de metadados da nuvem.

Lista de verificação completa e código: references/security.md.

Revisando um aplicativo existente

Quando a tarefa é auditar ou depurar em vez de construir, percorra references/ssr-review.md: verificações de modo de renderização, runes invisíveis no servidor, perigos de hidratação, estado com escopo de requisição, exposição de payload, buscas de segurança e exercícios de falha. Relate descobertas como arquivo:linha com a falha de produção que cada uma causa.

Arquivos de referência

  • references/runes-ssr.md — comportamento de cada rune no servidor, padrões de segurança para hidratação, estado com escopo de requisição, render + hydrate do svelte/server puro, e o status experimental de SSR assíncrono / funções remotas.
  • references/ssr-build.md — configuração do SvelteKit e adaptadores, opções de página (ssr/csr/prerender), hooks, form actions, streaming, Vite SSR puro e notas de implantação.
  • references/ssr-review.md — lista de verificação de auditoria para aplicativos SSR existentes: modos de renderização, runes invisíveis no servidor, perigos de hidratação, escopo de estado, exposição de payload, buscas de segurança e exercícios de falha.
  • references/security.md — segredos e variáveis de ambiente, CSP nonces, XSS, CSRF, manipulação de autenticação/sessão, SSRF, vazamento de erro e uma lista de verificação pré-implantação.
  • assets/ — arquivos iniciais copiáveis: svelte.config.js (SvelteKit com CSP), hooks.server.ts (autenticação mais cabeçalhos de segurança) e index.html / entry-server.js / entry-client.js para Vite SSR puro (o trio é construído em CI por tests/plain-ssr).

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

Habilidades Relacionadas