🎯 Svelte — Complete Cheatsheet 🎯
Svelte es un framework de compilación que traslada el trabajo reactivo a build time, generando código quirúrgico, altamente optimizado y sin Virtual DOM. Svelte 5 introduce el sistema de Runes ($state, $derived, $effect, $props, $bindable) para una reactividad explícita, predecible y escalable, eliminando la magia implícita de versiones anteriores. Este cheatsheet cubre desde la instalación y sintaxis moderna hasta gestión de estado avanzada, optimización de renders, SvelteKit integration y patrones de producción. Ideal para desarrolladores que migran desde React, Vue o Svelte 4 y buscan un framework ligero, rápido y con DX excepcional.
1. 🌟 Conceptos Fundamentales
- Compile-Time Reactivity: Svelte compila componentes a JavaScript plano que actualiza el DOM quirúrgicamente. Sin VDOM, sin diffing, sin overhead en runtime.
- Runes (Svelte 5+): Funciones especiales (
$state,$derived,$effect,$props) que declaran reactividad explícita. Se ejecutan en tiempo de compilación y garantizan granularidad. - Scoped CSS Nativo: Estilos se aplican automáticamente al componente con hashes únicos. Sin colisiones, sin necesidad de CSS Modules o Styled Components.
- Declarativo y Reactivo: La UI se deriva directamente del estado. Cambios en variables reactivas actualizan el DOM automáticamente.
- Component-Driven: Arquitectura basada en componentes autocontenidos con comunicación vía props, eventos y context.
- SvelteKit: Meta-framework oficial para routing, SSR/SSG, API routes, formularios y despliegue optimizado. Reemplaza la necesidad de Vite/Next/Nuxt por separado.
2.
Instalación y Entorno
- Scaffolding oficial:
npm create svelte@latest my-app # Selecciona: Skeleton / SvelteKit demo, TypeScript, ESLint, Prettier cd my-app && npm install - Estructura de proyecto (SvelteKit):
my-app/ ├── src/ │ ├── app.html # Plantilla HTML base │ ├── routes/ # File-based routing │ ├── lib/ # Componentes y utilidades compartidas │ └── app.css # Estilos globales ├── svelte.config.js # Configuración (adapters, preprocessors) ├── vite.config.ts # Integración con Vite └── package.json - Comandos esenciales:
npm run dev # Servidor de desarrollo con HMR npm run build # Compilación optimizada (SSR/SSG/SPA según adapter) npm run preview # Vista previa local del build npm run check # Type-check + validación Svelte (`svelte-check`)
3. 📝 Sintaxis y Reactividad (Svelte 5+ Runes)
3.1. $state y $derived
<script lang="ts">
let count = $state(0); // Estado reactivo mutable
let doubled = $derived(count * 2); // Valor computado (cacheado)
// Objetos y arrays son proxies profundos
let user = $state({ name: 'Ana', age: 28 });
let tags = $state(['svelte', 'frontend']);
function increment() {
count += 1; // ✅ Reactivo
user.age += 1; // ✅ Mutación directa (proxy)
tags.push('new'); // ✅ Métodos de array detectados
}
</script>
<p>Count: {count} | Double: {doubled}</p>
<button onclick={increment}>Increment</button>
3.2. $effect y $effect.pre
<script>
let width = $state(window.innerWidth);
let height = $state(window.innerHeight);
// Ejecuta después del DOM update (ideal para side-effects)
$effect(() => {
document.title = `Size: ${width}x${height}`;
});
// Ejecuta antes del DOM update (ideal para validación/sincronización)
$effect.pre(() => {
if (width < 640) console.log('Mobile view');
});
</script>
3.3. $props y $bindable
<script lang="ts">
// Props reactivas (tipo seguro con TS)
let { title, count = 0, onUpdate }: Props = $props();
// Prop bidireccional (sync con padre)
let { value }: { value: number } = $bindable();
</script>
<!-- Uso: <Counter title="Demo" bind:value={parentCount} /> -->
4. 🔀 Control de Flujo y Lógica
<script>
let items = $state([
{ id: 1, name: 'A', done: false },
{ id: 2, name: 'B', done: true }
]);
let promise = $state(fetch('/api/data'));
</script>
<!-- Condicional -->
{#if items.length > 0}
<ul>
{#each items as item (item.id)}
<li class:done={item.done}>{item.name}</li>
{:else}
<li>Vacío</li>
{/each}
</ul>
{/if}
<!-- Promesas / Async -->
{#await promise}
<p>Cargando...</p>
{:then data}
<pre>{JSON.stringify(data)}</pre>
{:catch error}
<p class="error">{error.message}</p>
{/await}
<!-- Key block (recrea DOM cuando cambia key) -->
{#key user.id}
<UserPro
{user} />
{/key}
5. 📦 Componentes, Props y Slots
5.1. Comunicación Padres-Hijos
<!-- Child.svelte -->
<script lang="ts">
let { label, value = '' }: { label: string; value: string } = $props();
let emit = $dispatch<'change'>();
function handleChange(e: Event) {
const target = e.target as HTMLInputElement;
emit('change', target.value);
}
</script>
<label>{label}</label>
<input value={value} oninput={handleChange} />
5.2. Slots y Fallbacks
<!-- Card.svelte -->
<div class="card">
<header>
<slot name="title">Default Title</slot>
</header>
<main>
<slot /> <!-- Slot por defecto -->
</main>
<footer>
<slot name="actions">
<button>Cerrar</button>
</slot>
</footer>
</div>
<!-- Uso -->
<Card>
<svelte:fragment slot="title">Mi Título</svelte:fragment>
<p>Contenido principal</p>
<svelte:fragment slot="actions">
<button>Guardar</button>
</svelte:fragment>
</Card>
6. ⚡ Efectos, Ciclo de Vida y Contexto
6.1. Lifecycle Hooks
<script>
import { onMount, onDestroy, tick } from 'svelte';
let canvas: HTMLCanvasElement;
onMount(async () => {
// Ejecuta tras montaje inicial
const ctx = canvas.getContext('2d');
ctx.fillRect(0, 0, 100, 100);
});
onDestroy(() => {
// Limpieza: clearInterval, removeEventListener, etc.
});
async function update() {
count++;
await tick(); // Espera a que el DOM se actualice
console.log('DOM actualizado:', count);
}
</script>
6.2. Context API (setContext / getContext)
<!-- Provider.svelte -->
<script>
import { setContext } from 'svelte';
let theme = $state('dark');
setContext('theme', { theme, toggle: () => theme = theme === 'dark' ? 'light' : 'dark' });
</script>
<slot />
<!-- Consumer.svelte -->
<script>
import { getContext } from 'svelte';
const { theme, toggle } = getContext('theme');
</script>
<p>Current: {theme}</p>
<button onclick={toggle}>Toggle</button>
7.
Routing y Full-Stack (SvelteKit)
7.1. Estructura de Rutas
src/routes/
├── +page.svelte # Página principal
├── +layout.svelte # Layout compartido (navbar, footer)
├── +error.svelte # Manejo de errores (4xx, 5xx)
├── api/
│ └── +server.ts # API endpoint (GET, POST, etc.)
└── [slug]/
└── +page.svelte # Ruta dinámica
7.2. Data Loading y Actions
// +page.server.ts (solo servidor)
import type { PageServerLoad, Actions } from './$types';
export const load: PageServerLoad = async ({ fetch }) => {
const res = await fetch('/api/users');
return { users: await res.json() };
};
export const actions: Actions = {
default: async ({ request }) => {
const data = await request.formData();
// Validar, guardar en DB, redirigir
return { success: true };
}
};
<!-- +page.svelte -->
<script lang="ts">
import type { PageData } from './$types';
import { invalidateAll } from '$app/navigation';
let { data }: { data: PageData } = $props();
let form = $state({ email: '', name: '' });
async function submit() {
const res = await fetch('/api/submit', {
method: 'POST', body: new FormData(form)
});
if (res.ok) await invalidateAll(); // Recarga datos del load
}
</script>
7.3. Navegación y Estado Global
import { goto, pushState, replaceState } from '$app/navigation';
import { page } from '$app/stores'; // $page.url, $page.data, $page.status
goto('/dashboard', { replaceState: true });
8. ⚠️ Errores Comunes y Trampas
- Mutación sin proxies: Modificar objetos fuera de
$stateo usarObject.freeze()rompe la reactividad. Fix: Declarar con$state()y mutar directamente (obj.key = val). - Confundir
$derivedcon$effect:$derivedes síncrono y cacheado.$effectes asíncrono y para side-effects. Fix: Usar$derivedpara valores calculados,$effectpara I/O, DOM o subscriptions. - Llamar APIs en
onMount: Rompe SSR y causa waterfalls. Fix: Usarloaden+page.server.tso+page.tspara fetching. - Slots sin fallbacks ni tipado: Causa errores en runtime si el padre no provee contenido.
Fix: Definir fallbacks explícitos y usar
<svelte:component>o interfaces para slots complejos. - Ignorar
tick()tras cambios de estado: Leer dimensiones/posiciones DOM inmediatamente después de mutar$statedevuelve valores viejos. Fix:await tick()antes de leer DOM o usarResizeObserver/MutationObserver. - Contexto no disponible en
onMount:getContext()falla si se llama antes de que el ancestro ejecutesetContext(). Fix: LlamargetContext()en el script top-level, no dentro de callbacks oonMount.
9.
Mejores Prácticas y Consejos de Experto
- Prefiere
$derivedsobre lógica manual: EliminauseMemo/useEffectboilerplate. La caché se invalida automáticamente cuando cambian dependencias. - Usa
svelte-checkystrictmode: Habilita TypeScript estricto entsconfig.json. Captura props faltantes, tipos incorrectos y accesibilidad en CI. - Centraliza formularios con
use:enhance:<form method="POST" use:enhance>maneja progresive enhancement, validación y redirección sin JS adicional. - Optimiza renders con
@consty destructuring:{@const name = user.firstName}evita recálculos en templates complejos. - Aprovecha
$inspect()para debugging:$inspect(count)imprime cambios en consola con stack trace. Ideal para tracear reactividad en desarrollo. - Mantén componentes pequeños y composables: Extraer lógica a
lib/o custom hooks (useAuth.ts,useDebounce.ts). Evitar componentes >300 líneas. - Usar
adapter-autopara despliegue: Detecta Vercel, Netlify, Cloudflare, Node automáticamente. Configuraradapter-verceloadapter-nodepara producción real. - Validar datos en el servidor: Nunca confiar en validación cliente. Usar
zodovalibotenloadyactionspara esquemas seguros. - Evitar side-effects en templates: No llamar funciones que mutan estado dentro de
{ }. Usar$derivedo handlersonclick. - Perfilar con DevTools:
npx svelte devtoolsoSvelte DevToolsextension. Inspeccionar renders, props, state y identificar actualizaciones innecesarias.
Este cheatsheet proporciona una referencia completa para Svelte, cubriendo el sistema de Runes moderno, reactividad explícita, control de flujo declarativo, gestión de estado avanzada, integración con SvelteKit y patrones de producción, junto con las mejores prácticas para construir interfaces rápidas, mantenibles y altamente optimizadas en entornos reales.