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.
Artigos relacionados
Claude Code para Angular: CLI, standalone components, formulários, HTTP e testes
Workflow com Claude Code para Angular CLI, componentes standalone, Reactive Forms, HttpClient, testes, E2E e revisão.
React com Claude Code em produção: componentes, Hooks, testes e revisão
Use Claude Code no React com componentes tipados, Hooks, testes, acessibilidade, performance e revisão.
Desenvolvimento com Storybook e Claude Code: UI, testes e CI
Configure Storybook com Claude Code: stories tipadas, interaction tests, CI e revisão visual com Chromatic.
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.