Hasta ahora todo ha sido bastante sencillo. Reutilizar componentes es muy fácil y nos da flexibilidad para reutilizar fragmentos de código en páginas diferentes. Sin embargo, puede ser insuficiente en ciertos casos.
Por ejemplo, imaginemos que en una página tenemos una variable con información que también debemos utilizar dentro de otro componente. Si tenemos que crear las variables en varios lugares, perdemos las ventajas de reutilizar componentes sin repetir información.
Para solucionarlo, existen las props y los slots.
Props: Compartir información
Imaginemos que en la página src/pages/index.astro
tenemos definida una variable con información (podría ser cualquier otro tipo de elemento u objeto). Esa información necesitamos utilizarla en otro componente... ¿Cómo la pasamos?
---
import SiteHeader from "../components/SiteHeader.astro";
const title = "El título de mi página";
---
<html>
...
<body>
<SiteHeader title={title} />
</body>
</html>
Observa que hemos creado la constante title
y luego en la parte del HTML, le hemos pasado esa información al componente <SiteHeader>
mediante el atributo title
.
Ahora veamos como recoger esa información denominada title
en nuestro fichero src/components/SiteHeader.astro
. Principalmente existen dos formas de hacerlo:
- 1️⃣ Creamos una variable
title
y en su interior ponemos el contenido deAstro.props.title
. - 2️⃣ Desestructuramos
title
deAstro.props
, creándose una variable con ese nombre.
---
const title = Astro.props.title; // 1️⃣ Forma simple
const { title, name } = Astro.props; // 2️⃣ Forma desestructurada
---
<p>{title}</p>
Sólo hace falta utilizar una de las dos. La primera es más sencilla, pero la última es más práctica, permitiendo añadir fácilmente más variables separadas por comas (como ocurre con name) si necesitaramos importar más información desde otros atributos.
Valores por defecto
Si lo deseas, puedes añadir valores por defecto a tus props, de modo que si no se ha pasado a ese componente un atributo con el valor, tomará el valor por defecto:
---
const title = Astro.props.title ?? "Default title";
const { title = "Default title", name = "ManzDev" } = Astro.props;
---
En ciertos casos puede interesarnos enviar varias props a otro componente, pero no queremos estar definiéndolos uno por uno. En ese caso, podemos hacer lo siguiente:
---
const data = {
title: "Mi título de página",
description: "Esta es la descripción de mi página.",
}
const { title, description } = data;
---
<!-- ❌ Molesto si tienes muchas props -->
<SiteHeader title={title} description={description} />
<!-- ✅ Más limpio: Envía todos los datos del objeto -->
<SiteHeader {...data} />
Slots: Ranuras de HTML
Igual que hemos utilizado las props para compartir información con los componentes hijos que utilizamos, podemos tener la necesidad de enviar información HTML desde el padre, donde las props pueden quedarse cortas. Para ello existen los Slots, una característica que existe en HTML nativo, y que Astro ha adaptado de una forma muy similar a su sistema de componentes.
Imaginemos que queremos mostrar una tarjeta de usuario, pero necesitamos que sea muy personalizable. El aspecto de esa página sería la siguiente:
---
import UserCard from "../components/UserCard.astro";
---
<UserCard name="ManzDev" image="manzdev.png">
<h2>Information</h2>
<p>...</p>
<h2>Links</h2>
<p>...</p>
</UserCard>
Observa que simplemente estamos importando el componente y utilizándolo, pasándole información por props. La diferencia es que en lugar de autocerrar el componente <UserCard />
, no lo cerramos y en su interior añadimos el contenido que se utilizará en su interior en el slot.
Veamos el componente en cuestión src/components/UserCard.astro
:
---
const { name, image } = Astro.props;
---
<div class="user-card">
<img src={image} alt={name} />
<slot></slot>
<p>Tarjeta de usuario.</p>
</div>
La etiqueta <slot />
será reemplazada exactamente con el contenido de la etiqueta <UserCard>
del ejemplo anterior.
Si nuestro componente
<UserCard>
no tiene contenido en su interior, se mostrará el fallback del slot, es decir, el contenido de<slot />
.
Slots múltiples
Si necesitas utilizar varios slots, puedes usar los slots múltiples. Funciona exactamente igual, pero en lugar de una sola etiqueta <slot>
puedes utilizar varias etiquetas <slot>
colocando un nombre en un atributo name
:
---
import UserCard from "../components/UserCard.astro";
---
<UserCard name="ManzDev" image="manzdev.png">
<slot name="header">
<h1>UserCard</h1>
</slot>
<slot name="content">
<h2>Information</h2>
<p>...</p>
<h2>Links</h2>
<p>...</p>
</slot>
<slot name="footer">Tarjeta de usuario personalizada.</slot>
</UserCard>
Luego, en el componente en cuestión src/components/UserCard.astro
, colocaremos la etiqueta contenedora con el atributo slot
con el nombre correspondiente:
---
const { name, image } = Astro.props;
---
<div class="user-card">
<header slot="header"></header>
<img src={image} alt={name} />
<main slot="content"></main>
<footer slot="footer">No hay información en el footer.</footer>
</div>
Lo que ocurrirá es que el contenido del <slot name="header">
se colocará en el interior de la etiqueta con atributo slot="header"
en nuestro componente.