Saltar al contenido

Fallback y slots avanzados

Cómo dar contenido de reserva a un hueco con un slot de apertura y cierre, comprobar en el frontmatter si un slot recibió contenido con Astro.slots.has, renderizar un slot a una cadena de HTML con Astro.slots.render para inspeccionarlo o transformarlo, y proyectar componentes completos como contenido hijo para anidar composiciones.

Fallback: el contenido de reserva

Un <slot /> autocerrado deja el hueco vacío si nadie lo rellena. Pero si le das etiqueta de apertura y cierre, lo que escribas dentro se convierte en su contenido de reserva: aparece solo cuando el padre no proyecta nada para ese hueco.

ASTRO
---// src/components/Boton.astro---<button type="button">  <slot>Aceptar</slot></button>

Si el padre escribe <Boton>Guardar</Boton>, gana Guardar; si escribe <Boton /> sin hijos, el botón muestra Aceptar. La semántica es de respaldo, no de suma: el contenido de reserva y el del padre nunca conviven, o uno o el otro. Funciona igual en un hueco con nombre, que puede llevar su propio respaldo dentro.

ASTRO
<header>  <slot name="titulo"><h1>Documento sin título</h1></slot></header>

El fallback es a los slots lo que el valor por defecto es a las props: una red de seguridad que hace opcional lo que de otro modo sería obligatorio. Con él, un componente rinde algo sensato incluso cuando lo usan a medias, y el diseño gana robustez sin que el padre tenga que acordarse de rellenar cada zona.

Astro.slots.has: interrogar el hueco

El fallback rellena un hueco vacío, pero a veces quieres lo contrario: no renderizar la estructura en absoluto cuando no hay contenido. Para eso, el frontmatter puede preguntar si un slot recibió algo con Astro.slots.has.

ASTRO
---// src/components/Panel.astroconst tienePie = Astro.slots.has('pie');---<section class="panel">  <slot />  {tienePie && (    <footer class="panel-pie">      <slot name="pie" />    </footer>  )}</section>

Astro.slots.has('pie') devuelve un booleano: ¿proyectó el padre contenido para pie? Con esa respuesta decides si el <footer> llega a existir. Ahí está la diferencia con el fallback: el respaldo llena el agujero con algo; has te deja eliminar el agujero entero, sin un <footer> vacío cuando no hay pie. El fallback responde a una pregunta —qué mostrar cuando el hueco llega vacío—; has responde a otra —si esa estructura debe existir siquiera—. Para el slot por defecto se consulta con el nombre default.

Astro.slots.render: el slot como valor

Astro.slots.render da el paso más radical: convierte el contenido proyectado en un valor. Devuelve una promesa que, al resolverse, entrega el HTML ya renderizado del slot como una cadena de texto.

ASTRO
---// src/components/Extracto.astroconst html = await Astro.slots.render('default');const texto = html.replace(/<[^>]+>/g, '');const palabras = texto.trim().split(/\s+/).filter(Boolean).length;---<article data-palabras={palabras}>  <Fragment set:html={html} /></article>

Con la cadena en la mano, el contenido deja de ser algo que solo colocas y pasa a ser algo que examinas: puedes medirlo, contar sus palabras, retirar sus etiquetas, transformarlo y devolverlo al DOM con set:html. Es el salto de posicionar contenido a inspeccionarlo. La contrapartida es que asumes la responsabilidad de reinyectarlo tú mismo: si no colocas el resultado con set:html, no aparece en ningún sitio.

Conviene medir el uso. Astro.slots.render obliga a esperar con await y a manejar una cadena, así que no sale gratis en complejidad; recúrrelo cuando de verdad necesites el contenido como dato, no como atajo para colocarlo. Para simplemente mostrar lo proyectado, <slot /> sigue siendo la herramienta correcta y más barata.

Componentes como contenido hijo

Nada obliga a que los hijos sean HTML nativo. Un componente entero cabe como contenido proyectado, y ahí la composición despliega toda su fuerza.

ASTRO
---// src/pages/index.astroimport Modal from '../components/Modal.astro';import Boton from '../components/Boton.astro';---<Modal>  <h2 slot="titulo">Confirmar borrado</h2>  <p>Esta acción no se puede deshacer.</p>  <Boton slot="acciones">Borrar</Boton></Modal>

Boton se renderiza en el ámbito de la página y aterriza en el hueco acciones de Modal, que ni conoce ni necesita conocer qué componente ha recibido. Este es el mecanismo detrás de un layout que envuelve una página que envuelve widgets: son slots hasta el fondo. Cada capa aporta estructura y cede contenido, y la composición se anida tan hondo como el diseño pida, sin que ninguna capa dependa de las que aloja.

Anidar composiciones es solo apilar esta misma operación. Un Layout con su <slot /> envuelve una página, y esa página envuelve a su vez componentes en los slots de otras piezas, formando una pila donde cada nivel presta estructura al de dentro.

ASTRO
---// src/pages/acerca.astroimport Layout from '../layouts/Base.astro';import Tarjeta from '../components/Tarjeta.astro';---<Layout>  <Tarjeta>    <h2 slot="cabecera">Sobre nosotros</h2>    <p>Un equipo pequeño construyendo herramientas sobrias.</p>  </Tarjeta></Layout>