Guía exhaustiva para dominar Svelte, el framework revolucionario que compila a JavaScript vanilla.
Svelte es un compilador que convierte componentes en JavaScript eficiente durante el build, eliminando la necesidad de un Virtual DOM.
# Crear proyecto con Vite (recomendado)
npm create vite@latest mi-app-svelte --template svelte
cd mi-app-svelte
npm install
# Alternativa con degit (plantilla oficial)
npx degit sveltejs/template mi-app-svelte
cd mi-app-svelte
npm install
<script>
let count = 0;
function increment() {
count += 1;
}
</script>
<main>
<h1>Hola Mundo Svelte!</h1>
<p>Contador: {count}</p>
<button on:click={increment}>
Incrementar
</button>
</main>
<style>
h1 {
color: #ff3e00;
}
</style>
En Svelte, la reactividad es automática cuando se asignan valores:
<script>
let name = 'Mundo';
let counter = 0;
// Función reactiva
$: doubled = counter * 2;
// Declaración reactiva
$: {
console.log(`El contador es: ${counter}`);
}
</script>
<input bind:value={name}>
<p>Hola {name}!</p>
<button on:click={() => counter++}>
Clics: {counter} (Doble: {doubled})
</button>
<script>
export let name = 'Mundo'; // Prop con valor por defecto
</script>
<p>Hola, {name}!</p>
<script>
import Greeting from './components/Greeting.svelte';
let userName = 'Juan';
</script>
<Greeting name={userName} />
Svelte permite enlace bidireccional con bind:
:
<script>
let name = '';
let isChecked = false;
let selectedOption = 'A';
</script>
<input bind:value={name} placeholder="Tu nombre">
<p>Hola, {name || 'anónimo'}</p>
<input type="checkbox" bind:checked={isChecked}>
<p>Checkbox {isChecked ? 'marcado' : 'desmarcado'}</p>
<select bind:value={selectedOption}>
<option value="A">Opción A</option>
<option value="B">Opción B</option>
</select>
<p>Seleccionado: {selectedOption}</p>
<script>
function handleClick(event) {
alert('Botón clickeado!');
}
let mousePosition = { x: 0, y: 0 };
function handleMousemove(event) {
mousePosition = { x: event.clientX, y: event.clientY };
}
</script>
<button on:click={handleClick}>
Haz clic
</button>
<div on:mousemove={handleMousemove}>
Mueve el mouse aquí: {mousePosition.x}, {mousePosition.y}
</div>
Svelte ofrece stores para manejar estado global:
import { writable } from 'svelte/store';
export const count = writable(0);
// Métodos:
// count.set(n) - Establecer valor
// count.update(n => n + 1) - Actualizar basado en valor anterior
// count.subscribe(callback) - Suscribirse a cambios
<script>
import { count } from '../stores/counter';
function increment() {
count.update(n => n + 1);
}
</script>
<button on:click={increment}>
Clics: {$count}
</button>
import { readable } from 'svelte/store';
export const time = readable(new Date(), set => {
const interval = setInterval(() => {
set(new Date());
}, 1000);
return () => clearInterval(interval);
});
import { derived } from 'svelte/store';
import { time } from './time';
export const elapsed = derived(
time,
$time => Math.floor($time / 1000)
);
<script>
import { fade, fly } from 'svelte/transition';
import { elasticOut } from 'svelte/easing';
let visible = true;
</script>
<button on:click={() => visible = !visible}>
Toggle
</button>
{#if visible}
<div transition:fade={{ duration: 500 }}>
Aparece/Desaparece
</div>
<div transition:fly={{
y: 50,
duration: 800,
easing: elasticOut
}}>
Vuela hacia arriba/abajo
</div>
{/if}
<div class="card">
<slot name="header">Título por defecto</slot>
<slot>Contenido por defecto</slot>
<slot name="footer"></slot>
</div>
<Card>
<h1 slot="header">Título personalizado</h1>
<p>Contenido personalizado</p>
<button slot="footer">Acción</button>
</Card>
<script>
import { setContext } from 'svelte';
import Child from './Child.svelte';
setContext('theme', 'dark');
</script>
<Child />
<script>
import { getContext } from 'svelte';
const theme = getContext('theme');
</script>
<p>Theme: {theme}</p>
export function longpress(node, duration) {
let timer;
const handleMousedown = () => {
timer = setTimeout(() => {
node.dispatchEvent(new CustomEvent('longpress'));
}, duration);
};
const handleMouseup = () => {
clearTimeout(timer);
};
node.addEventListener('mousedown', handleMousedown);
node.addEventListener('mouseup', handleMouseup);
return {
destroy() {
node.removeEventListener('mousedown', handleMousedown);
node.removeEventListener('mouseup', handleMouseup);
}
};
}
<script>
import { longpress } from './actions/longpress.js';
function handleLongpress() {
alert('Long press detected!');
}
</script>
<button
use:longpress={500}
on:longpress={handleLongpress}
>
Mantén presionado
</button>
import { writable } from 'svelte/store';
function createAuthStore() {
const { subscribe, set } = writable(null);
return {
subscribe,
login: (user) => set(user),
logout: () => set(null)
};
}
export const auth = createAuthStore();
<script>
import { auth } from '../stores/auth';
</script>
{#if $auth}
<p>Bienvenido, {$auth.name}</p>
<button on:click={() => auth.logout()}>
Cerrar sesión
</button>
{:else}
<button on:click={() => auth.login({ name: 'Usuario' })}>
Iniciar sesión
</button>
{/if}
<script>
let isActive = true;
let error = false;
</script>
<div class:active={isActive} class:error>
Clases dinámicas
</div>
<!-- Equivalente a: -->
<div class={`${isActive ? 'active' : ''} ${error ? 'error' : ''}`}>
Clases dinámicas
</div>
<script>
import ComponentA from './ComponentA.svelte';
import ComponentB from './ComponentB.svelte';
let components = {
a: ComponentA,
b: ComponentB
};
let current = 'a';
</script>
<select bind:value={current}>
<option value="a">Componente A</option>
<option value="b">Componente B</option>
</select>
<svelte:component this={components[current]} />
<script>
import { onMount } from 'svelte';
let modal;
onMount(() => {
modal = document.createElement('div');
document.body.appendChild(modal);
return () => {
document.body.removeChild(modal);
};
});
</script>
<svelte:component this={ModalContent} {close} bind:this={modal} />
Svelte soporta Server-Side Rendering (SSR) con SvelteKit:
<script context="module">
export async function load({ fetch }) {
const res = await fetch('/api/data');
const data = await res.json();
return {
props: { data }
};
}
</script>
<script>
export let data;
</script>
<p>Datos cargados en el servidor: {data.message}</p>
SvelteKit es el framework oficial para construir aplicaciones Svelte:
npm create svelte@latest mi-app-sveltekit
cd mi-app-sveltekit
npm install
npm run dev
src/
├── app.html # Plantilla HTML base
├── routes/
│ ├── +page.svelte # Página principal
│ ├── about/
│ │ └── +page.svelte # /about
│ └── blog/
│ ├── +page.svelte # /blog
│ └── [slug]/
│ └── +page.svelte # /blog/:slug
├── lib/ # Código reutilizable
└── app.d.ts # Tipos globales
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
<slot /> <!-- Contenido de las páginas -->
<script>
import { page } from '$app/stores';
</script>
<h1>Post: {$page.params.slug}</h1>
<script context="module">
export async function load({ fetch }) {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return {
props: { posts }
};
}
</script>
<script>
export let posts;
</script>
{#each posts as post}
<article>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
{/each}
<script>
import { enhance } from '$app/forms';
let form;
</script>
<form method="POST" use:enhance>
<input name="email" type="email" bind:value={form.email}>
<textarea name="message" bind:value={form.message}></textarea>
<button type="submit">Enviar</button>
</form>
export const actions = {
default: async ({ request }) => {
const formData = await request.formData();
const email = formData.get('email');
const message = formData.get('message');
// Procesar el formulario...
return {
success: true
};
}
};
import { sequence } from '@sveltejs/kit/hooks';
async function auth({ event, resolve }) {
const session = event.cookies.get('session');
event.locals.user = session ? getUser(session) : null;
return await resolve(event);
}
export const handle = sequence(auth);
export function load({ locals }) {
return {
user: locals.user
};
}
SvelteKit soporta múltiples adaptadores:
# Adaptador para Node.js
npm install @sveltejs/adapter-node
# Adaptador para Vercel
npm install @sveltejs/adapter-vercel
# Adaptador para Static Site
npm install @sveltejs/adapter-static
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/kit/vite';
export default {
kit: {
adapter: adapter()
},
preprocess: vitePreprocess()
};
# Desarrollo
npm run dev
# Producción
npm run build
npm run preview
# Static Site Generation
npm run build
npm run preview