Runes en Svelte: Guía Completa con Ejemplos

¿Qué son las Runes en Svelte?

Las Runes en Svelte son el sistema de reactividad introducido en Svelte 5. Son objetos especiales que almacenan y administran valores reactivos de manera más eficiente que los anteriores stores. Permiten declarar variables reactivas usando la sintaxis $ (por ejemplo: let $count = 0), y cualquier cambio en su valor actualiza automáticamente todos los componentes que lo utilizan.

Características

  Características clave de los runes

  • Modularidad: Los runes encapsulan lógica reutilizable que se puede compartir entre componentes.
  • Reactividad: Los runes están diseñados para interactuar con la reactividad de Svelte, actualizando automáticamente los valores enlazados.
  • Reutilizables: Puedes crear tus propios runes y compartirlos entre proyectos.
  • Simplicidad: Permiten gestionar comportamientos complejos de manera más clara y organizada.

Tipos de Runes en Svelte

  Tipos de Runes

  • Los $props(): son inmutables y se usan para recibir datos del componente padre
  • El $state(): es perfecto para datos que cambian con el tiempo
  • El $derived(): calcula valores automáticamente basándose en otros estados
  • El $effect(): es ideal para manejar efectos secundarios como llamadas API o persistencia

Props Runes ($props)

Los props son valores que se pasan desde el componente padre y son inmutables por defecto Esta rune permite acceder y reaccionar a las propiedades (props) que recibe un componente.

Ejemplo 1: Props básicos para un componente de botón

js
const buttonProps = $props();

const label = buttonProps.label || 'Botón';
const variant = buttonProps.variant || 'primary';
html
<button class={variant}>
{label}
</button>
  ✅ Descripción:

  • Los props label y variant se obtienen con valores predeterminados en caso de que no sean proporcionados.
  • Esto lo hace reutilizable para diferentes estilos y etiquetas de botones.

Ejemplo 2: Props con validación

js
const userProps = $props((props) => {
if (!props.name || !props.email || typeof props.age !== 'number') {
  throw new Error('Props inválidos: se requieren name, email y age como número.');
}
if (props.age < 0) {
  throw new Error('La edad debe ser un número positivo.');
}
return props;
});
html
<p>Nombre: {userProps.name}</p>
<p>Edad: {userProps.age}</p>
<p>Email: {userProps.email}</p>
  ✅ Descripción:

  • La función de validación asegura que name, age y email sean válidos antes de usarlos en el componente.
  • Los errores evitan que se muestren datos mal formados.

Ejemplo 3: Props con valores por defecto

js
const cardProps = $props({
title: 'Título por defecto',
description: 'Sin descripción proporcionada',
image: null,
});

const { title, description, image } = cardProps;
html
<div class="card">
<h1>{title}</h1>
<p>{description}</p>
{#if image}
  <img src={image} alt={title} />
{/if}
</div>
  ✅ Descripción:

  • Usa valores predeterminados para title y description si no se pasan.
  • El condicional en {#if image} asegura que solo se renderice la imagen si está definida.

Ejemplo 4: Acceder y mostrar todas las props de un componente: Puedes usar $props() para acceder dinámicamente a todas las propiedades que se pasan al componente.

js
<script>
import { $props } from 'svelte';

const props = $props();
</script>

<h1>Props recibidas:</h1>
<ul>
{#each Object.entries(props) as [key, value]}
  <li>{key}: {value}</li>
{/each}
</ul>
html
<!-- Uso -->
<Componente nombre="Svelte" version="5" />
<!--Muestra todas las props con sus valores en forma de lista.--->

Ejemplo 5: Reaccionar dinámicamente al cambio de props: Usa el ciclo de vida afterUpdate para reaccionar dinámicamente al cambio de props, actualizando el estado interno o ejecutando lógica cuando cambien.

js
<script>
import { afterUpdate } from 'svelte';

export let nombre;

afterUpdate(() => {
  console.log(`Prop nombre cambió a: ${nombre}`);
});
</script>

<h1>Hola, {nombre}!</h1>

<!-- Uso -->
<Componente nombre="Svelte" />
<!-- Cuando la prop `nombre` cambia, `afterUpdate` se ejecuta y registra el nuevo valor.-->

Ejemplo 6: Combinar $props() con valores predeterminados: Establece valores predeterminados para props opcionales si no se pasan.

js
<script>
import { $props } from 'svelte';

const props = $props();
const nombre = props.nombre || 'Desarrollador';
const edad = props.edad || 25;
</script>

<h1>Hola, {nombre}!</h1>
<p>Tu edad es {edad} años.</p>

<!-- Uso -->
<Componente nombre="Svelte" />
<!-- Si no se pasa edad, usará 25 como predeterminado -->

Si una prop no se pasa, se usa el valor predeterminado.

Ejemplos de Contador básico

Variable reactiva $count que se incrementa en 1 y actualiza automáticamente toda la UI donde se use.

js
// 1. Contador básico
let $count = 0;
function increment() {
$count++;
}

Ejemplos de Toggle booleano

Estado reactivo $isVisible que alterna entre true/false para controlar la visibilidad de elementos en la UI.

js
// 2. Toggle booleano
let $isVisible = false;
function toggle() {
$isVisible = !$isVisible;
}

Objeto reactivo

Objeto $user con propiedades reactivas que mantiene sincronizada la UI cuando se modifica cualquier propiedad.

js
// 3. Objeto reactivo
let $user = {
name: 'Ana',
age: 25
};
function updateAge() {
$user.age++;
}

Lista reactiva

Array $todos que mantiene una lista de tareas y se actualiza de forma inmutable para preservar la reactividad.

js
// 4. Lista reactiva
let $todos = [];
function addTodo(text) {
$todos = [...$todos, { text, completed: false }];
}

Valor derivado

Combina reactivamente $firstName y $lastName para crear $fullName que se actualiza cuando cambia cualquier nombre.

js
// 5. Valor derivado (computed)
let $firstName = 'Juan';
let $lastName = 'Pérez';
let $fullName = $derived(`${$firstName} ${$lastName}`);

Estado con validación

Campo $email con validación reactiva que verifica automáticamente el formato cada vez que cambia el valor.

js
// 6. Estado con validación
let $email = '';
let $isValid = $derived(() => {
return $email.includes('@') && $email.length > 5;
});

Derived Runes ($derived) para Estados Derivados

Con $derived, puedes crear estados que dependan de otros, recalculándose automáticamente cuando los estados originales cambian. En conclusión con runes $derived, podemos calcular valores automáticamente a partir de otros estados.

Ejemplo 1: Valor derivado simple

js
let price = $state(100)
let tax = $state(0.21)
let totalPrice = $derived(price + (price * tax))

Calcula el precio total incluyendo impuestos automáticamente

Ejemplo 2: Lista filtrada

js
let items = $state(['manzana', 'banana', 'cereza'])
let searchTerm = $state('')
let filteredItems = $derived(
items.filter(item => item.includes(searchTerm))
)

Filtra una lista basada en un término de búsqueda

Ejemplo 3: Estadísticas

js
let scores = $state([75, 80, 95, 70])
let average = $derived(
scores.reduce((a, b) => a + b, 0) / scores.length
)

Calcula el promedio de una lista de puntuaciones

Ejemplo 4: Cálculo Automático con $derived en Listas

js
let numbers = $state([1, 2, 3, 4]);
let total = $derived(numbers.reduce((t, n) => t + n, 0));

Ahora podemos usar esto en nuestro marcado:

js
<p>{numbers.join(' + ')} = {total}</p>

La expresión dentro de la declaración $derived se volverá a evaluar cada vez que numbers actualice sus dependencias. A diferencia del estado normal ($state), el estado derivado es de solo lectura.

Ejemplo 5: Cálculo Automático con $derived en Svelte Archivo: store.js:

js
import { $state, $derived } from 'svelte';

export const basePrice = $state(100);
export const quantity = $state(2);
export const totalPrice = $derived(() => basePrice.get() * quantity.get());

Uso en el Componente App.svelte:

js
<script>
import { basePrice, quantity, totalPrice } from './store.js';
</script>

<p>Precio Base: {basePrice}</p>
<p>Cantidad: {quantity}</p>
<p>Precio Total: {totalPrice}</p>

<button on:click={() => quantity.set(quantity.get() + 1)}>Incrementar Cantidad</button>

Effect Runes ($effect) para Efectos Secundarios

La rune $effect ejecuta código automáticamente cuando un estado cambia. Es decir permite ejecutar efectos secundarios cuando un estado cambia.

Ejemplo 1: Logging de cambios

js
let username = $state('')
$effect(() => {
console.log(`Username changed to: ${username}`)
})

Registra cambios en el nombre de usuario

Ejemplo 2: Persistencia local

js
let preferences = $state({ theme: 'light' })
$effect(() => {
localStorage.setItem('preferences', JSON.stringify(preferences))
})

Guarda preferencias en localStorage cuando cambian

Ejemplo 3: Llamadas API

js
let userId = $state(null)
$effect(() => {
if (userId) {
  fetch(`/api/users/${userId}`)
    .then(response => response.json())
    .then(data => console.log(data))
}
})

Realiza una llamada API cuando cambia el ID del usuario

Ejemplo 2: Seguimiento de Cambios con $effect en Svelte Archivo: logger.js:

python
import { $effect } from 'svelte';
import { globalCount } from './store.js';

$effect(() => {
console.log('El contador global cambió a:', globalCount.get());
});

Uso en el Componente App.svelte:

python
<script>
import './logger.js';
import { globalCount } from './store.js';
</script>

<button on:click={() => globalCount.set(globalCount.get() + 1)}>Incrementar</button>

Uso de Runes Fuera de Componentes en Svelte


Las runes en Svelte también se pueden usar fuera de los componentes para manejar estados globales, acciones compartidas o cualquier funcionalidad que necesite ser accesible en múltiples partes de tu aplicación.

STATE RUNES ($state) para un Estado Global

La rune $state nos permite crear estados globales accesibles desde cualquier parte de la aplicación. En conclusión runes $state representa un estado local reactivo que puede cambiar durante la vida del componente

Ejemplo 1: Contador simple

js
let count = $state(0)
function increment() {
count++
}

Estado básico para un contador interactivo

Ejemplo 2: Estado de formulario

js
let formData = $state({
username: '',
password: '',
isValid: false
})

Maneja múltiples campos de formulario en un solo estado

Ejemplo 3: Estado de toggle

js
let isOpen = $state(false)
function toggle() {
isOpen = !isOpen
}

Control de estado booleano para mostrar/ocultar elementos