Desenvolvimento Svelte e SvelteKit com Claude Code
Guia pratico para usar Claude Code com Svelte/SvelteKit: setup, runes, rotas, form actions, testes e mudancas seguras.
Por que Claude Code combina com SvelteKit
Svelte e um framework de UI que compila componentes declarativos para JavaScript enxuto. SvelteKit adiciona a camada de aplicacao: rotas por arquivos, renderizacao no servidor, funcoes load, form actions, endpoints e adaptadores de deploy. Em um projeto real, uma pequena funcionalidade costuma tocar src/routes, src/lib/components, $lib/server, tipos gerados e testes.
Claude Code e uma ferramenta agentica de desenvolvimento capaz de ler o repositorio, editar arquivos e executar comandos. Em SvelteKit, o ganho aparece quando a tarefa e bem delimitada: primeiro ler os arquivos, depois explicar o fluxo de dados, propor o plano e so entao alterar o menor conjunto possivel. Pedidos amplos como “crie um SaaS em SvelteKit” misturam UI, banco, autenticacao, seguranca e deploy de uma vez.
Para iniciantes, o ponto critico e Svelte 5. Runes sao a forma atual de escrever reatividade: $props recebe entradas do componente, $state declara estado mutavel, $derived calcula valores a partir do estado e $effect deve ficar para efeitos de navegador. Este artigo usa um pequeno app de tarefas para mostrar setup, componentes, estado compartilhado, rotas, formularios, testes, prompts seguros e armadilhas.
flowchart LR
A["Escrever um requisito pequeno"] --> B["Claude Code le arquivos relacionados"]
B --> C["Editar componentes Svelte"]
C --> D["Verificar load/actions"]
D --> E["Rodar checks e testes"]
E --> F["Revisar git diff manualmente"]
Setup do projeto
Para criar um app SvelteKit novo, o caminho oficial atual comeca com a CLI do Svelte: npx sv create my-app. Para um app Svelte independente com Vite, o template svelte-ts tambem e valido. O guia atual do Vite exige Node.js 20.19+ ou 22.12+ para o Vite, entao atualize o Node antes de investigar erros estranhos de build.
npx sv create claude-svelte-demo
cd claude-svelte-demo
npm install
npm run dev
claude
Em um repositorio novo, comece pelo plan mode. Claude Code pode explorar os arquivos e escrever um plano sem editar o codigo-fonte.
/plan
Quero adicionar uma lista de tarefas a este projeto SvelteKit.
Primeiro leia src/routes e src/lib, depois proponha os arquivos a mudar, o fluxo de dados e o plano de testes.
Ainda nao edite arquivos.
claude --permission-mode plan
Regras do projeto devem ficar em CLAUDE.md ou .claude/CLAUDE.md: gerenciador de pacotes, comandos de validacao, preferencia por Svelte 5 runes, uso de form actions, limite de arquivos, segredo apenas em $lib/server e nenhum commit sem pedido explicito. Regras concretas sao melhores do que “deixe o codigo bonito”.
Componentes pequenos com Svelte 5
O componente abaixo pode ser usado como src/lib/components/TaskCard.svelte. Ele recebe uma tarefa e uma funcao onToggle, calcula textos com $derived e mantem aria-pressed no botao.
<!-- src/lib/components/TaskCard.svelte -->
<script lang="ts">
type Task = {
id: string;
title: string;
done: boolean;
estimateMinutes: number;
tags: string[];
};
let {
task,
onToggle
}: {
task: Task;
onToggle: (id: string) => void;
} = $props();
let statusLabel = $derived(task.done ? 'Concluida' : 'Aberta');
let estimateLabel = $derived(`${Math.ceil(task.estimateMinutes / 15) * 15} min`);
</script>
<article class:done={task.done} class="task-card">
<div>
<p class="status">{statusLabel}</p>
<h3>{task.title}</h3>
<p>{estimateLabel}</p>
</div>
<ul aria-label="Tags">
{#each task.tags as tag}
<li>{tag}</li>
{/each}
</ul>
<button type="button" aria-pressed={task.done} onclick={() => onToggle(task.id)}>
{task.done ? 'Reabrir' : 'Marcar como concluida'}
</button>
</article>
<style>
.task-card {
display: grid;
gap: 0.75rem;
border: 1px solid #ddd;
border-radius: 0.5rem;
padding: 1rem;
}
.done {
background: #f2fff5;
}
.status {
font-size: 0.875rem;
font-weight: 700;
}
ul {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
list-style: none;
padding: 0;
}
li {
border-radius: 999px;
background: #eef2ff;
padding: 0.2rem 0.6rem;
}
</style>
Prompt seguro:
Melhore src/lib/components/TaskCard.svelte mantendo a sintaxe Svelte 5 runes.
Condicoes:
- Nao altere o tipo Task nem a assinatura onToggle.
- Mantenha onclick e nao volte para on:click.
- Mantenha aria-pressed no botao.
- Melhore apenas layout e estados vazios.
- Depois sugira um teste de componente.
Esse nivel de detalhe impede que Claude Code altere a API publica do componente ou mexa em arquivos fora do escopo.
Estado compartilhado: runes primeiro, stores quando necessario
Svelte 5 permite usar runes em arquivos .svelte.ts. Eles sao bons para logica reativa reutilizavel e estado compartilhado. svelte/store ainda faz sentido para fluxos assincronos complexos, inscricoes manuais ou integracao com bibliotecas que ja usam stores.
// src/lib/state/taskFilters.svelte.ts
export type TaskStatus = 'all' | 'open' | 'done';
export const taskFilters = $state({
query: '',
status: 'all' as TaskStatus,
tag: ''
});
export function resetTaskFilters() {
taskFilters.query = '';
taskFilters.status = 'all';
taskFilters.tag = '';
}
<!-- src/lib/components/TaskFilterPanel.svelte -->
<script lang="ts">
import { resetTaskFilters, taskFilters } from '$lib/state/taskFilters.svelte';
</script>
<section aria-label="Filtros de tarefas">
<label>
Palavra-chave
<input bind:value={taskFilters.query} placeholder="Fatura, artigo, revisao..." />
</label>
<label>
Status
<select bind:value={taskFilters.status}>
<option value="all">Todas</option>
<option value="open">Abertas</option>
<option value="done">Concluidas</option>
</select>
</label>
<button type="button" onclick={resetTaskFilters}>Resetar</button>
</section>
O erro comum e esquecer SSR. window, document e localStorage nao existem no servidor. Para persistir algo no navegador, use browser de $app/environment.
// src/lib/state/theme.svelte.ts
import { browser } from '$app/environment';
export const themeState = $state({
theme: 'system' as 'system' | 'light' | 'dark'
});
export function loadTheme() {
if (!browser) return;
const saved = localStorage.getItem('theme');
if (saved === 'light' || saved === 'dark' || saved === 'system') {
themeState.theme = saved;
}
}
export function saveTheme(nextTheme: typeof themeState.theme) {
themeState.theme = nextTheme;
if (browser) localStorage.setItem('theme', nextTheme);
}
Rotas, load e fronteira do servidor
SvelteKit usa roteamento por arquivos. src/routes/about cria /about; src/routes/tasks/[slug] cria uma rota dinamica com parametro slug. Se a pagina precisa de dados antes de renderizar, use +page.server.ts. Banco de dados, chaves privadas e APIs privilegiadas devem ficar em $lib/server.
// src/lib/server/tasks.ts
export type Task = {
id: string;
slug: string;
title: string;
done: boolean;
estimateMinutes: number;
tags: string[];
};
const tasks: Task[] = [
{
id: 'task-1',
slug: 'write-svelte-guide',
title: 'Rascunhar o artigo de SvelteKit',
done: false,
estimateMinutes: 45,
tags: ['writing', 'svelte']
}
];
export async function getTaskBySlug(slug: string) {
return tasks.find((task) => task.slug === slug) ?? null;
}
// src/routes/tasks/[slug]/+page.server.ts
import { error } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
import { getTaskBySlug } from '$lib/server/tasks';
export const load: PageServerLoad = async ({ params }) => {
const task = await getTaskBySlug(params.slug);
if (!task) {
error(404, 'Task not found');
}
return { task };
};
<!-- src/routes/tasks/[slug]/+page.svelte -->
<script lang="ts">
import type { PageProps } from './$types';
let { data }: PageProps = $props();
</script>
<svelte:head>
<title>{data.task.title} | Tasks</title>
</svelte:head>
<article>
<p>{data.task.done ? 'Concluida' : 'Aberta'}</p>
<h1>{data.task.title}</h1>
<p>Estimativa: {data.task.estimateMinutes} minutos</p>
</article>
Leia src/routes/tasks/[slug] e src/lib/server/tasks.ts.
Adicione um campo dueDate ao Task e mostre na pagina de detalhe.
Mantenha acesso a dados do servidor em $lib/server.
Nao renomeie o diretorio [slug].
Execute npm run check no final.
Form actions e melhoria progressiva
SvelteKit form actions permitem exportar actions de +page.server.ts e enviar um <form method="POST"> ao servidor. O formulario funciona sem JavaScript; use:enhance melhora a experiencia quando JavaScript esta disponivel.
// src/routes/contact/+page.server.ts
import { fail } from '@sveltejs/kit';
import type { Actions } from './$types';
export const actions = {
default: async ({ request }) => {
const formData = await request.formData();
const values = {
name: String(formData.get('name') ?? '').trim(),
email: String(formData.get('email') ?? '').trim(),
message: String(formData.get('message') ?? '').trim()
};
const errors: Record<string, string> = {};
if (values.name.length < 2) errors.name = 'Digite pelo menos 2 caracteres.';
if (!values.email.includes('@')) errors.email = 'Confira o email.';
if (values.message.length < 10) errors.message = 'Digite pelo menos 10 caracteres.';
if (Object.keys(errors).length > 0) {
return fail(400, { values, errors });
}
console.log('New inquiry', values);
return { success: true };
}
} satisfies Actions;
<!-- src/routes/contact/+page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
import type { PageProps } from './$types';
let { form }: PageProps = $props();
</script>
{#if form?.success}
<p role="status">Enviado. Responderemos em 1 a 3 dias uteis.</p>
{/if}
<form method="POST" use:enhance>
<label>
Nome
<input name="name" value={form?.values?.name ?? ''} aria-invalid={!!form?.errors?.name} />
</label>
{#if form?.errors?.name}<p>{form.errors.name}</p>{/if}
<label>
Email
<input name="email" type="email" value={form?.values?.email ?? ''} aria-invalid={!!form?.errors?.email} />
</label>
{#if form?.errors?.email}<p>{form.errors.email}</p>{/if}
<label>
Mensagem
<textarea name="message" rows="5" aria-invalid={!!form?.errors?.message}>{form?.values?.message ?? ''}</textarea>
</label>
{#if form?.errors?.message}<p>{form.errors.message}</p>{/if}
<button type="submit">Enviar</button>
</form>
Nao use GET para efeitos colaterais, nao exponha chaves privadas no cliente e nao renderize HTML nao confiavel com {@html} sem sanitizacao. No prompt, escreva que a validacao no servidor, o funcionamento sem JavaScript e os atributos de acessibilidade devem permanecer.
Testes, casos de uso e armadilhas
A documentacao de testes do Svelte aponta Vitest como uma boa escolha para projetos Vite e SvelteKit. Testing Library ajuda a testar comportamento de usuario; Playwright fica para fluxos completos.
// src/lib/components/TaskCard.test.ts
import { fireEvent, render, screen } from '@testing-library/svelte';
import { describe, expect, it } from 'vitest';
import TaskCard from './TaskCard.svelte';
describe('TaskCard', () => {
it('toggles the task when the button is clicked', async () => {
let toggledId = '';
render(TaskCard, {
task: {
id: 'task-1',
title: 'Escrever o artigo de SvelteKit',
done: false,
estimateMinutes: 45,
tags: ['writing']
},
onToggle: (id) => {
toggledId = id;
}
});
await fireEvent.click(screen.getByRole('button', { name: 'Marcar como concluida' }));
expect(toggledId).toBe('task-1');
});
});
npm run check
npm run test
npm run build
git diff -- src/lib src/routes
| Caso de uso | Bom trabalho para Claude Code | Revisao humana |
|---|---|---|
| Filtros admin | Separar filtros com $state e $derived | Condicoes na URL e campos por permissao |
| Blog ou CMS | Conectar [slug], load, SEO e 404 | Sanitizacao HTML, rascunhos e preview |
| Formularios de leads | Actions, validacao, use:enhance, testes | Privacidade, spam, destino da notificacao |
| Migracao Svelte 4 para 5 | Converter componentes escolhidos para runes | Evitar mudancas grandes sem revisao |
As falhas comuns sao pedir tudo de uma vez, misturar sintaxe Svelte 4 e 5, importar $lib/server em componentes, usar $effect para calculos que deveriam ser $derived, e aceitar testes fracos. Para monetizacao, SvelteKit conecta conteudo tecnico a formularios e consultoria; veja a pagina de consultoria Claude Code. Continue com o guia inicial, dicas TypeScript e estrategias de teste.
Fontes oficiais e resultado testado
Use fontes oficiais quando o comportamento importar: Svelte docs, SvelteKit docs, SvelteKit form actions, Vite guide e Claude Code docs.
Leia a estrutura atual do SvelteKit e altere apenas este escopo.
Arquivos: src/routes/contact/+page.svelte e src/routes/contact/+page.server.ts
Objetivo: adicionar campo company ao formulario.
Restricoes:
- Manter Svelte 5 runes.
- Nao remover use:enhance.
- Adicionar validacao no servidor.
- Nao alterar texto de CTA nem classes de layout.
- Executar npm run check antes de terminar.
Finalize com 3 linhas: mudancas, risco e testes faltantes.
Masa testou esse fluxo em um app pequeno de tarefas com SvelteKit. Comecar em plan mode reduziu retrabalho. As instrucoes “manter runes”, “executar npm run check” e “nao fazer commit” produziram diffs menores e mais faceis de revisar.
PDF grátis: cheatsheet do Claude Code
Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.
Cuidamos dos seus dados e não enviamos spam.
Sobre o autor
Masa
Engenheiro focado em workflows práticos com Claude Code.
Artigos relacionados
Workflow Obsidian para CLAUDE.md com Claude Code
Transforme notas de trabalho do Obsidian em notas operacionais CLAUDE.md para preservar contexto.
Claude Code Revenue CTA Routing: artigos para PDF, Gumroad e consultoria
Um fluxo com Claude Code para levar leitores ao PDF grátis, Gumroad ou consultoria conforme intenção.
Regras de handoff para equipes com Claude Code: evidências, permissões, rollback e receita
Formato prático para entregar trabalho do Claude Code com prova, permissões, rollback, PDF grátis, Gumroad e consultoria.