En el artículo anterior aprendimos la sintaxis básica de los componentes de Astro. Hoy vamos a ver cómo usarlos y su utilidad.
Hemos dicho que Astro (al igual que React, Vue o Svelte), basa su arquitectura en componentes. ¿Pero eso que significa?
Imagina que hicieras una web con 50 páginas (o 5000 como esta que estás leyendo). No parece muy práctico tener que copiar y pegar el menú de navegación, el footer y las tarjetas de artículos etc etc… 50 veces ¿Verdad que no?
Aquí es donde entra el concepto de Componentización.
Un componente es un bloque de código reutilizable que encapsula su propia estructura (HTML), estilo (CSS) y lógica (JS).
Vamos a aprender a crear estos bloques, y como hacerlos reusables pasándoles información (Props) y contenido (Slots).
Creando nuestro primer componente
Vamos a verlo con un ejemplo, creando un botón reutilizable. En lugar de escribir <button class="btn">Click</button> cada vez que lo necesitemos, nos creamos un componente.
Para ello simplemente creamos un fichero src/components/Boton.astro:
---
// Aquí iría la lógica si la necesitáramos
---
<button class="mi-boton">
¡Soy un componente!
</button>
<style>
.mi-boton {
background-color: #4F46E5;
color: white;
padding: 0.5rem 1rem;
border-radius: 0.25rem;
border: none;
cursor: pointer;
}
</style>
Ahora, para usarlo en una página (por ejemplo index.astro), solo tenemos que importarlo:
---
import Boton from '../components/Boton.astro';
---
<h1>Bienvenido a mi web</h1>
<Boton />
<Boton />
Fíjate que la etiqueta <style> está dentro del componente. En Astro, los estilos tienen ámbito local (scoped) por defecto.
Esto significa que esa clase .mi-boton no afectará a otros botones de la web fuera de este componente.
Props: Pasando datos al componente
El botón anterior es un bastante inútil porque siempre dice “¡Soy un componente!”. A nosotros nos gustaría poder cambiar ese texto cada vez que insertemos el botón.
Necesitamos que sea dinámico y pasarle información. Para eso usamos las Props (propiedades).
Las props son los atributos que pasamos al componente (igual que el src o alt en una etiqueta <img>).

Por ejemplo, vamos a mejorar nuestro botón para
---
// Desestructuramos las props para usarlas fácilmente
// Asignamos un valor por defecto a 'esPrimario' si no viene informado
const { texto, esPrimario = true } = Astro.props;
---
<button class:list={['base', { primario: esPrimario, secundario: !esPrimario }]}>
{texto}
</button>
<style>
.base { padding: 10px 20px; border: none; cursor: pointer; }
.primario { background: blue; color: white; }
.secundario { background: gray; color: black; }
</style>
En Astro, las props se reciben a través del objeto global Astro.props en el frontmatter. Podemos usar las props como variables durante el build time.
<Boton texto="Guardar" />
<Boton texto="Cancelar" esPrimario={false} />
Slots: Inyectando contenido HTML
Las Props están genial para pasar datos simples (strings, números, booleanos). Pero, ¿qué pasa si quiero que mi botón contenga un icono, o texto en negrita, o un párrafo entero?
Pasar HTML a través de una prop es complicado, y mala idea. Para eso existen los Slots (huecos).
El elemento <slot /> es un marcador de posición que le dice a Astro: “Aquí es donde debes meter cualquier cosa que pongan dentro de las etiquetas de mi componente”.
Para entenderlo mejor, vamos a mejorar nuestro botón para que acepte cualquier contenido, no solo un texto plano.
---
const { tipo = 'button' } = Astro.props;
---
<button type={tipo} class="btn">
<slot />
</button>
Ahora es mucho más flexible, porque el elemento que lo usa puede cambiar todo el contenido del boton por un fragmento cualquier de HTML.
<Boton>
Hacer <strong>Click</strong>
</Boton>
<Boton>
<img src="/icono.svg" /> Guardar
</Boton>
Contenido por defecto (Fallback)
Puedes poner contenido dentro de las etiquetas <slot>Texto por defecto</slot>.
Este contenido solo se mostrará si el usuario utiliza el componente vacío <Boton />.
Slots nombrados
¿Y si necesitamos inyectar contenido en varios lugares diferentes de un mismo componente? El ejemplo clásico es una tarjeta (Card) que tiene un encabezado, un cuerpo y un pie.
Para esto usamos los Slots Nombrados. Usamos el atributo name en la etiqueta <slot>.
---
const { titulo } = Astro.props;
---
<article class="card">
<header>
<h2>{titulo}</h2>
<slot name="header-icon" />
</header>
<div class="content">
<slot />
</div>
<footer>
<slot name="footer" />
</footer>
</article>
Ahora para enviar contenido a un slot específico, usamos el atributo slot="nombre-del-slot" en el elemento HTML que inyectamos.
---
import Card from '../components/Card.astro';
---
<Card titulo="Aprendiendo Astro">
<span slot="header-icon">🚀</span>
<p>Este es el contenido principal de la tarjeta.</p>
<p>Podemos poner varios párrafos.</p>
<button slot="footer">Leer más</button>
</Card>
