Saltar al contenido

El slot por defecto y la composición

Cómo un componente recibe contenido hijo mediante el marcador de slot, en qué se diferencia la composición del paso de props, dónde se sitúa el hueco dentro de la plantilla del hijo y por qué el contenido proyectado se evalúa en el ámbito léxico del padre que lo redacta y no del hijo que lo aloja.

Contenido hijo: lo que va entre las etiquetas

Hasta ahora instanciabas componentes con la etiqueta autocerrada, como <Saludo />. Pero un componente admite también etiqueta de apertura y de cierre, y todo lo que escribas entre ambas es su contenido hijo: un fragmento de plantilla que el padre redacta y confía al hijo para que lo aloje.

ASTRO
---// src/pages/index.astroimport Aviso from '../components/Aviso.astro';---<Aviso>  <p>Tu sesión caduca en cinco minutos.</p></Aviso>

Ese <p> no es una prop. No viaja por un atributo ni aparece en Astro.props; es marcado que el padre deposita dentro del hijo. Puedes proyectar varios nodos a la vez —varios elementos, texto suelto, incluso otros componentes— y todos entran juntos, en el orden en que los escribiste. Pero aquí surge la primera regla implacable: si Aviso no hace nada para recogerlo, el contenido se descarta en silencio. Escribir hijos no basta; el hijo tiene que reclamarlos.

Conviene una distinción fina antes de seguir. <Aviso /> autocerrado y <Aviso></Aviso> vacío son equivalentes: ninguno proyecta nada, y el hijo se comporta como si nadie le hubiera pasado contenido. La etiqueta de cierre no obliga a rellenar el hueco, solo abre la posibilidad; se usa cuando hay algo que alojar dentro y se omite —autocerrando— cuando el componente se configura únicamente por props.

El marcador <slot />

El hijo reclama su contenido con un elemento especial: <slot />. Funciona como un marcador de posición que Astro sustituye por lo que el padre haya colocado entre las etiquetas.

ASTRO
---// src/components/Aviso.astro---<aside class="aviso" role="status">  <slot /></aside>

El componente aporta la envoltura —el <aside>, su clase, su rol de accesibilidad— y reserva con <slot /> el hueco por donde entrará lo de fuera. El resultado combina ambas capas: la estructura del hijo por fuera, el contenido del padre por dentro. Coloca el marcador en el punto exacto que tu estructura destina al contenido ajeno; su posición en la plantilla es toda su responsabilidad, porque el <slot /> no decide qué recibe, solo dónde aparece.

La envoltura que rodea al hueco puede ser tan rica como quieras: varios elementos anidados, clases, atributos de accesibilidad, incluso otros componentes. El <slot /> es solo el punto de inyección dentro de esa envoltura, no un límite para ella. Un componente puede tener una cabecera fija, un pie fijo y, en medio, el hueco por donde entra lo que cambia de un uso a otro.

Composición frente a props

Props y slots resuelven necesidades distintas, y confundirlas produce diseños torpes. Una prop transporta un dato —un valor tipado y atómico que el hijo interpreta— bajo un contrato estrecho. Un slot transporta contenido —marcado arbitrario, quizá con otros componentes dentro— que el hijo no interpreta, solo ubica.

La regla práctica es directa. Si el hijo necesita entender el valor —compararlo con una lista, meterlo en un atributo, recorrerlo—, es una prop. Si el hijo solo necesita envolver algo cuya forma no le incumbe —una tarjeta que rodea cualquier cuerpo, un botón que muestra cualquier etiqueta—, es un slot. El estado que gobierna una variante entra por props; el interior de un modal entra por slot.

Lo más común es que un mismo componente use ambos canales a la vez. Un botón recibe por props su variante —un dato que interpreta para elegir una clase— y por slot su etiqueta —contenido que solo muestra sin mirarlo.

ASTRO
---// src/components/Boton.astrointerface Props {  variante?: 'solida' | 'fantasma';}const { variante = 'solida' } = Astro.props;---<button class={`btn-${variante}`}>  <slot /></button>

El padre escribe <Boton variante="fantasma">Cancelar</Boton>: variante viaja por el atributo y termina traducida en una clase; Cancelar viaja por el hueco y termina como texto visible. Cada canal hace lo suyo y el componente los combina sin confundirlos. Reconocer qué parte de una pieza es dato y qué parte es contenido es, en el fondo, todo el arte de diseñar su interfaz.

Este molde —una envoltura de estructura fija con un hueco central— reaparece en casi toda biblioteca de componentes: la tarjeta, el modal, el aviso, el acordeón, el propio botón. Todos comparten la misma anatomía, props para lo que interpretan y un slot para lo que albergan, y por eso, en cuanto entiendes el patrón en uno, lo reconoces en todos.

El ámbito del contenido proyectado

Queda una pregunta sutil pero decisiva: cuando el contenido hijo usa una variable, ¿de quién es esa variable, del padre o del hijo? La respuesta define la mitad del modelo mental de los slots.

ASTRO
---// src/pages/perfil.astroimport Panel from '../components/Panel.astro';const usuario = 'Ada';---<Panel>  <h2>Hola, {usuario}</h2></Panel>

El {usuario} de ese <h2> se resuelve con la constante usuario de la página, porque el contenido hijo se redacta en la página y se evalúa con su ámbito léxico. Aunque Panel definiera dentro su propio usuario, no influiría: el hijo solo elige la posición del fragmento, mientras que los nombres que ese fragmento usa se buscan siempre arriba, donde fue escrito. El hijo hereda el marcado ya resuelto, no el código que lo produjo.

Esta separación de ámbitos es lo que hace componibles a los slots. El padre controla el qué —los datos y el marcado del contenido—; el hijo controla el dónde —la posición dentro de su estructura—. Ninguno invade el terreno del otro, y por eso un mismo envoltorio sirve a padres que no comparten ni una sola variable.

Este mismo principio, escalado hasta la raíz del árbol, es lo que hará posibles los layouts: un componente que envuelve páginas enteras con su <slot /> y les presta cabecera, pie y documento. Lo verás en detalle más adelante; por ahora basta con reconocer que un layout no es más que el caso mayor de esta misma idea, una envoltura con un hueco.