Use Cases (Actualizado: 2/6/2026)

Desarrollo Svelte y SvelteKit con Claude Code

Guia practica para usar Claude Code con Svelte/SvelteKit: setup, runes, rutas, form actions, pruebas y cambios seguros.

Desarrollo Svelte y SvelteKit con Claude Code

Por que Claude Code funciona bien con SvelteKit

Svelte es un framework de UI que compila componentes declarativos a JavaScript ligero. SvelteKit anade la capa de aplicacion: rutas basadas en archivos, renderizado en servidor, funciones load, form actions, endpoints y adaptadores de despliegue. En una app real, una pequena funcionalidad suele tocar src/routes, src/lib/components, $lib/server, tipos generados y pruebas. Por eso Claude Code encaja bien: puede leer el repo completo, editar varios archivos y ejecutar comandos, pero necesita limites claros.

La mejor forma de usarlo no es pedir “crea una app completa”. Es mejor pedir: “lee esta ruta, explica el flujo de datos, propone el cambio y despues modifica solo estos archivos”. Ese estilo es especialmente importante con Svelte 5, porque los ejemplos antiguos de internet todavia muestran export let y $:. En proyectos nuevos conviene usar runes: $props para entradas del componente, $state para estado mutable, $derived para valores calculados y $effect solo para efectos de navegador.

Este articulo usa una app pequena de tareas para cubrir setup, componentes, estado compartido, rutas, formularios, pruebas, prompts seguros para Claude Code, casos de uso y errores comunes. La idea es que un principiante pueda copiar los ejemplos y tambien revisar el diff con criterio.

flowchart LR
  A["Escribir un requisito pequeno"] --> B["Claude Code lee archivos relacionados"]
  B --> C["Editar componentes Svelte"]
  C --> D["Revisar load/actions"]
  D --> E["Ejecutar checks y pruebas"]
  E --> F["Revisar git diff manualmente"]

Setup del proyecto

Para crear una app SvelteKit nueva, el camino oficial actual empieza con la CLI de Svelte: npx sv create my-app. Si solo necesitas una app Svelte independiente sobre Vite, el template svelte-ts de Vite tambien es valido. La guia actual de Vite pide Node.js 20.19+ o 22.12+ para Vite, asi que conviene actualizar Node antes de culpar a Svelte por errores del toolchain.

npx sv create claude-svelte-demo
cd claude-svelte-demo
npm install
npm run dev

claude

En un repo nuevo, empieza con plan mode. En este modo Claude Code investiga y propone cambios sin editar el codigo fuente.

/plan
Quiero agregar una lista de tareas a este proyecto SvelteKit.
Primero lee src/routes y src/lib, luego propone los archivos a cambiar, el flujo de datos y el plan de pruebas.
Todavia no edites archivos.
claude --permission-mode plan

Las reglas del proyecto deben vivir en CLAUDE.md o .claude/CLAUDE.md: paquete preferido, comandos de validacion, uso de Svelte 5 runes, convenciones de form actions, limites de seguridad y “no hacer commit salvo que se pida”. Las reglas concretas funcionan mejor que frases vagas como “escribe buen codigo”.

Componentes pequenos con Svelte 5

El componente siguiente puede copiarse como src/lib/components/TaskCard.svelte. Mantiene un contrato pequeno: recibe una tarea y una funcion onToggle. Los textos derivados se calculan con $derived, y el boton conserva aria-pressed para accesibilidad.

<!-- 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 ? 'Completada' : 'Pendiente');
  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="Etiquetas">
    {#each task.tags as tag}
      <li>{tag}</li>
    {/each}
  </ul>

  <button type="button" aria-pressed={task.done} onclick={() => onToggle(task.id)}>
    {task.done ? 'Marcar pendiente' : 'Marcar completada'}
  </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>

Un buen prompt para mejorar este componente protege su API:

Mejora src/lib/components/TaskCard.svelte manteniendo la sintaxis de Svelte 5 runes.
Condiciones:
- No cambies el tipo Task ni la firma onToggle.
- Mantén onclick y no lo conviertas a on:click.
- Mantén aria-pressed en el boton.
- Solo mejora layout y estados vacios.
- Al final sugiere una prueba de componente.

Esto evita que Claude Code convierta una mejora visual en un refactor amplio. Tambien ayuda a revisar el diff porque los cambios quedan dentro de un componente.

Estado compartido: runes primero, stores cuando aportan valor

Svelte 5 permite usar runes en archivos .svelte.ts. Sirven para logica reactiva reutilizable y estado compartido. svelte/store sigue siendo util cuando necesitas flujos asincronos complejos, suscripciones manuales o integracion con librerias que ya usan el contrato de 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 tareas">
  <label>
    Palabra clave
    <input bind:value={taskFilters.query} placeholder="Factura, articulo, revision..." />
  </label>

  <label>
    Estado
    <select bind:value={taskFilters.status}>
      <option value="all">Todas</option>
      <option value="open">Pendientes</option>
      <option value="done">Completadas</option>
    </select>
  </label>

  <button type="button" onclick={resetTaskFilters}>Restablecer</button>
</section>

La trampa habitual es olvidar SSR. window, document y localStorage solo existen en el navegador. Si necesitas persistir una preferencia, usa browser desde $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);
}

Rutas, load y codigo de servidor

SvelteKit usa rutas por sistema de archivos. src/routes/about crea /about; src/routes/tasks/[slug] crea una ruta dinamica. Cuando la pagina necesita datos antes de renderizar, usa +page.server.ts o +page.ts. La regla practica es sencilla: base de datos, claves privadas y APIs privilegiadas se quedan en $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: 'Redactar el articulo 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 ? 'Completada' : 'Pendiente'}</p>
  <h1>{data.task.title}</h1>
  <p>Estimacion: {data.task.estimateMinutes} minutos</p>
</article>

Prompt recomendado:

Lee src/routes/tasks/[slug] y src/lib/server/tasks.ts.
Agrega un campo dueDate a Task y muestralo en la pagina de detalle.
Mantén el acceso a datos del servidor dentro de $lib/server.
No renombres el directorio [slug].
Ejecuta npm run check al final.

Form actions y mejora progresiva

Las form actions de SvelteKit permiten exportar actions desde +page.server.ts y enviar un <form method="POST"> al servidor. El formulario funciona sin JavaScript, y use:enhance mejora la experiencia cuando JavaScript esta disponible.

// 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 = 'Escribe al menos 2 caracteres.';
    if (!values.email.includes('@')) errors.email = 'Revisa el correo.';
    if (values.message.length < 10) errors.message = 'Escribe al 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 en 1 a 3 dias laborables.</p>
{/if}

<form method="POST" use:enhance>
  <label>
    Nombre
    <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>
    Mensaje
    <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>

No uses GET para efectos secundarios, no expongas claves privadas en el cliente y no renderices HTML no confiable con {@html} sin sanitizar. Al pedir cambios a Claude Code, escribe “mantén validacion del servidor”, “funciona sin JavaScript” y “no elimines atributos de accesibilidad”.

Pruebas, casos de uso y errores comunes

La documentacion de Svelte describe Vitest como una opcion natural para proyectos Vite y SvelteKit. Testing Library funciona bien para componentes porque obliga a probar comportamiento visible para el usuario.

// 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: 'Escribir el articulo de SvelteKit',
        done: false,
        estimateMinutes: 45,
        tags: ['writing']
      },
      onToggle: (id) => {
        toggledId = id;
      }
    });

    await fireEvent.click(screen.getByRole('button', { name: 'Marcar completada' }));
    expect(toggledId).toBe('task-1');
  });
});
npm run check
npm run test
npm run build
git diff -- src/lib src/routes
Caso de usoTrabajo adecuado para Claude CodeRevision humana
Filtros de adminSeparar estado con $state y vistas con $derivedFiltros en URL y campos ocultos por permisos
Blog o CMSConectar [slug], load, SEO y 404Sanitizado HTML, borradores y previews
Formularios de leadsActions, validacion, use:enhance, pruebasPrivacidad, spam, destino de notificaciones
Migracion Svelte 4 a 5Convertir componentes concretos a runesEvitar cambios de comportamiento masivos

Los fallos mas comunes son: pedir demasiado en una sola instruccion, mezclar sintaxis Svelte 4 y 5, importar $lib/server desde componentes, usar $effect para calculos que deberian ser $derived, y aceptar pruebas debiles que solo comprueban que un boton existe.

Para monetizacion, SvelteKit permite conectar articulos tecnicos con formularios y consultoria. Si tu equipo necesita ayuda real de rollout, usa la pagina de consultoria de Claude Code. Tambien puedes seguir con la guia de inicio, los consejos TypeScript y las estrategias de testing.

Fuentes oficiales y resultado probado

Para decisiones importantes, consulta fuentes oficiales: Svelte docs, SvelteKit docs, SvelteKit form actions, Vite guide y Claude Code docs.

Lee la estructura actual de SvelteKit y cambia solo este alcance.
Archivos: src/routes/contact/+page.svelte y src/routes/contact/+page.server.ts
Objetivo: agregar un campo company al formulario.
Restricciones:
- Mantener Svelte 5 runes.
- No eliminar use:enhance.
- Agregar validacion del servidor.
- No cambiar el texto CTA ni las clases de layout.
- Ejecutar npm run check antes de terminar.
Termina con 3 lineas: cambios, riesgo y pruebas faltantes.

Al probar este flujo en una app pequena de tareas con SvelteKit, Masa vio menos retrabajo cuando empezo en plan mode. Especificar “mantener runes”, “ejecutar npm run check” y “no hacer commit” produjo diffs pequenos y faciles de revisar.

#Claude Code #Svelte #SvelteKit #frontend #TypeScript
Gratis

PDF gratis: cheatsheet de Claude Code

Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.

Cuidamos tus datos y no enviamos spam.

Masa

Sobre el autor

Masa

Ingeniero enfocado en workflows prácticos con Claude Code.