Etiqueta slot en HTML: contenido distribuido en Shadow DOM
La etiqueta <slot> define un punto de inserción para contenido externo en el árbol de sombra de un componente web. Sirve, por ejemplo, para crear una tarjeta cuya estructura y estilos pertenecen al componente, mientras quien la utiliza proporciona el título y la descripción. Úsala cuando necesites distribuir contenido dentro de Shadow DOM y conservar ese contenido como hijos del elemento que aloja el componente.
<template id="plantilla-tarjeta">
<article>
<slot name="titulo">Sin título</slot>
<slot></slot>
</article>
</template>
Resultado: este fragmento no muestra una tarjeta. <template> guarda contenido inerte; por sí solo no crea un árbol de sombra ni registra un componente. Para probar el mínimo, guárdalo dentro del <body> de un documento HTML y ábrelo: la plantilla estará en el DOM, pero su contenido no aparecerá. El ejemplo completo de abajo añade el código que la utiliza.
Sintaxis y contexto válido
Escribe <slot></slot> con apertura y cierre: ninguna de las dos etiquetas es omisible. En un archivo HTML, <slot /> no cierra el elemento. Puedes darle un nombre y contenido de respaldo, también llamado fallback:
<slot name="titulo"><h2>Sin título</h2></slot>
<slot><p>No hay descripción.</p></slot>
El modelo de contenido de slot es transparente: el contenido de respaldo debe respetar el contexto que rodea al slot. Por ejemplo, no pongas un <h2> de respaldo si el slot está dentro de un <p>. El elemento se admite donde se espera contenido de frase; eso no convierte cualquier ubicación en un punto de distribución operativo.
Para distribuir nodos, el slot debe estar en un árbol de sombra. El elemento al que se une ese árbol se llama host o anfitrión. Sus hijos ordinarios forman parte del light DOM; la estructura interna del componente pertenece al Shadow DOM. En el ejemplo, el host será <tarjeta-taller-slot>.
Slot por defecto y slot con nombre
Con la asignación automática utilizada aquí, el navegador compara name del slot con el atributo global slot de los hijos directos del host:
<tarjeta-taller-slot>
<h2 slot="titulo">Taller de HTML</h2>
<p>Aprende a organizar una página.</p>
</tarjeta-taller-slot>
El encabezado se presenta en <slot name="titulo">; el párrafo, en el slot sin nombre. Un name ausente o vacío identifica el slot por defecto. Los elementos sin slot, o con slot="", y los nodos de texto sin nombre pueden asignarse a él. Usa nombres iguales, incluidas mayúsculas y minúsculas: titulo y Titulo son distintos.
La asignación no mueve ni clona los hijos del host. El <h2> sigue siendo hijo de <tarjeta-taller-slot> y los selectores del documento pueden encontrarlo allí. El navegador los presenta conjuntamente con el árbol de sombra. Tampoco busca cualquier descendiente: un encabezado metido dentro de un <div> hijo no se asigna por su propio slot="titulo"; debes asignar el hijo directo adecuado. Estas reglas proceden de la asignación de slots del DOM Standard.
El fallback aparece cuando ese slot no tiene nodos asignados. No comprueba si el texto resulta útil: un elemento asignado vacío lo sustituye igualmente. En el slot por defecto, incluso un salto de línea entre las etiquetas del host puede contar como un nodo de texto y ocultar el respaldo. Por eso el host vacío del ejemplo se escribe sin espacios internos. Si repites un nombre en varios slots, la asignación automática elige el primero; no duplica el contenido.
Ejemplo práctico: una tarjeta de taller
Guarda este documento como tarjetas-slot.html, en UTF-8, y ábrelo en un navegador. Incluye todo el HTML, CSS y JavaScript; no necesita archivos externos ni un servidor.
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Tarjetas con slots</title>
<style>
body {
margin: 0;
padding: 1.5rem;
font: 1rem/1.5 system-ui, sans-serif;
color: #162b45;
background: #f7fafc;
}
main { max-inline-size: 42rem; margin-inline: auto; }
tarjeta-taller-slot { display: block; margin-block: 1rem; }
a:focus-visible {
outline: 3px solid #155fc1;
outline-offset: 3px;
}
</style>
</head>
<body>
<main>
<h1>Próximos talleres</h1>
<template id="plantilla-taller-slot">
<style>
:host { display: block; }
article {
padding: 1rem;
border: 1px solid #61748a;
border-radius: 0.5rem;
background: white;
}
h2, ::slotted(h2) { margin-block: 0 0.5rem; }
::slotted(p) {
padding-inline-start: 0.5rem;
border-inline-start: 3px solid #155fc1;
}
</style>
<article>
<slot name="titulo"><h2>Sin título</h2></slot>
<slot><p>Próximamente publicaremos la descripción.</p></slot>
</article>
</template>
<tarjeta-taller-slot id="taller-con-datos">
<h2 slot="titulo">Taller de HTML</h2>
<p>Aprende a organizar una página <strong>con ejemplos</strong>.</p>
<a href="#inscripcion">Consultar inscripción</a>
</tarjeta-taller-slot>
<p>La segunda tarjeta permite comprobar los textos de respaldo:</p>
<tarjeta-taller-slot id="taller-vacio"></tarjeta-taller-slot>
<section id="inscripcion">
<h2>Inscripción</h2>
<p>Esta muestra no recoge ni envía datos.</p>
</section>
<noscript>
<p>Sin JavaScript puedes leer el primer taller y consultar
la inscripción. La tarjeta vacía no genera textos de respaldo.</p>
</noscript>
</main>
<script>
(() => {
if (!("customElements" in window) ||
!("attachShadow" in Element.prototype)) return;
if (customElements.get("tarjeta-taller-slot")) return;
const plantilla = document.getElementById("plantilla-taller-slot");
class TarjetaTallerSlot extends HTMLElement {
constructor() {
super();
const raiz = this.attachShadow({ mode: "open" });
raiz.append(plantilla.content.cloneNode(true));
}
}
customElements.define("tarjeta-taller-slot", TarjetaTallerSlot);
})();
</script>
</body>
</html>
El script está después de la plantilla y los hosts. Encierra sus variables en una función y comprueba el registro para evitar definir dos veces el mismo custom element. Cada instancia crea su raíz con attachShadow() y recibe una copia de la plantilla, no una copia del contenido proporcionado. mode: "open" permite inspeccionar la raíz mediante host.shadowRoot.
Resultado con JavaScript: la primera tarjeta muestra el encabezado, la descripción con un borde azul y el enlace. La segunda muestra «Sin título» y «Próximamente publicaremos la descripción». El título va antes de la descripción porque los slots están en ese orden dentro de la plantilla.
Prueba los cambios en este orden, recargando el archivo entre ellos:
- Pulsa Tab para llegar a Consultar inscripción. Comprueba el foco visible y pulsa Enter: el enlace lleva a la sección Inscripción.
- Retira el
<h2 slot="titulo">del primer host. Aparece Sin título, pero la descripción sigue asignada. Si solo cambias su atributo aslot="otro", aparece el mismo respaldo y el encabezado queda sin un slot que lo muestre. - Deja el primer host completamente vacío, sin espacios internos. Deben aparecer ambos textos de respaldo, como en la segunda tarjeta.
- Retira de la plantilla el slot con nombre completo. El título proporcionado sigue en el light DOM, pero deja de mostrarse en la tarjeta. Retira también el slot por defecto: tampoco se muestran la descripción ni el enlace.
- Desactiva JavaScript y recarga. Se ven el título, el párrafo y el enlace originales del primer host, en su orden HTML. No se crea el árbol de sombra, no aparecen sus bordes ni los fallbacks de la tarjeta vacía. La información del taller y la inscripción siguen disponibles.
Estilos: hasta dónde llega ::slotted()
Dentro de la raíz de sombra, article selecciona el artículo interno. Una regla h2 alcanza el encabezado de respaldo de esa raíz; para el encabezado suministrado desde fuera se utiliza ::slotted(h2). El pseudo-elemento ::slotted() de CSS selecciona elementos asignados al slot, no todos sus descendientes ni los nodos de texto sueltos.
En la muestra, ::slotted(p) aplica el borde al párrafo. No selecciona por separado su <strong>. ::slotted(p strong) no es un selector válido para atravesar ese contenido: su argumento admite un selector compuesto, sin combinadores. Para dar estilo al <strong>, puedes usar desde el documento tarjeta-taller-slot p strong, porque esos nodos permanecen en el light DOM. Propiedades heredables, como color, sí pueden llegar a los descendientes por herencia.
Quita los estilos de la plantilla para comprobar que desaparece la presentación de tarjeta, pero continúa la distribución. Conserva los estilos de foco del documento. Cambiar CSS no reemplaza los slots ni aporta el significado de los encabezados.
Atributos, métodos y evento
name es el atributo específico de <slot>: acepta una cadena y nombra el punto de inserción. En JavaScript, la propiedad .name del elemento refleja ese atributo. slot es un atributo global del contenido que se asigna; no es otro nombre para name. También puedes usar globales como id, class o lang cuando corresponda.
assignedNodes() es un método DOM, no un atributo HTML. Devuelve los nodos asignados; puede incluir texto. Sin opciones devuelve una lista vacía si no hay asignación, aunque se esté viendo el fallback. La opción { flatten: true } permite obtener el respaldo cuando no hay asignados y resolver slots anidados. Para comprobar el título del ejemplo en la consola:
const host = document.getElementById("taller-con-datos");
const punto = host.shadowRoot.querySelector('slot[name="titulo"]');
punto.assignedNodes(); // Contiene el h2 del host.
slotchange es un evento: permite reaccionar a cambios en la asignación, por ejemplo al retirar un hijo o cambiar su atributo slot. No equivale a observar todas las ediciones dentro de los nodos asignados. ::slotted() pertenece a CSS; tampoco es un atributo ni un método JavaScript.
Accesibilidad y orden de lectura
El slot no crea por sí mismo un encabezado, una etiqueta de control ni una región accesible. Conserva elementos nativos en el contenido: el <h2> del taller sigue identificando un encabezado de nivel dos. El componente no ajusta automáticamente su nivel a la página; elige la jerarquía según el contexto. Consulta la guía de encabezados h1 a h6.
Mantén coherentes el orden del light DOM y el orden de distribución. No uses slots ni CSS para colocar visualmente un control antes de sus instrucciones si la lectura o el recorrido de teclado quedan distintos. En este ejemplo se mantiene título, descripción y enlace. Comprueba el encabezado, el texto y el enlace con las herramientas de accesibilidad y con lector de pantalla en los navegadores que debas cubrir; no presupongas anuncios idénticos entre combinaciones.
Preguntas frecuentes y errores
¿Slot funciona en cualquier div?
Poner <slot> dentro de un <div> ordinario no activa la distribución. Un <div> sí puede ser host si le creas una raíz con attachShadow() y colocas allí los slots. No todos los elementos admiten esa API. Si solo necesitas agrupar contenido de una página, usa un contenedor adecuado sin añadir Shadow DOM.
Si el contenido no aparece, comprueba primero que existe la raíz de sombra, que la plantilla se clonó dentro de ella y que el nodo es hijo directo del host. Después compara slot y name, y verifica que el punto de inserción no se haya retirado. Si falta el fallback, revisa si hay nodos asignados vacíos o espacios de texto.
¿Es igual al slot de Astro?
Los slots de componentes Astro insertan contenido al renderizar el componente, durante la compilación de un sitio estático o en el servidor si la página se renderiza bajo demanda. Astro interpreta <slot /> en archivos .astro; esa sintaxis no implica que el navegador reciba una raíz de sombra ni un slot nativo operativo.
En HTML nativo, la distribución la realiza el navegador sobre un árbol de sombra. Copiar el slot de un layout Astro a un archivo .html no crea ese árbol. Astro puede entregar un custom element como el de esta guía, pero entonces su funcionamiento nativo depende del código y la estructura de ese elemento.
¿Cuándo usar template o script?
<template> guarda la estructura reutilizable; <slot> señala dónde presentar contenido ajeno a esa estructura. No son intercambiables. En este caso, <script> ejecuta el registro y crea Shadow DOM. También existe Shadow DOM declarativo con <template shadowrootmode="open">, cuyo contexto y soporte deben comprobarse aparte; no lo activa el id de nuestra plantilla.
Compatibilidad y fuentes
Consulta de compatibilidad: 3 de octubre de 2026. MDN clasifica <slot>, attachShadow() básico, assignedNodes() y slotchange como funciones ampliamente disponibles en navegadores actuales. Esa disponibilidad no garantiza todas las opciones nuevas de Shadow DOM; el ejemplo utiliza asignación automática y una raíz abierta, sin APIs de asignación manual.
Las reglas de distribución se contrastaron con árboles de sombra del DOM Standard; sintaxis y modelo de contenido, con el HTML Living Standard. Para los límites del selector se consultó CSS Shadow Module. Verifica por separado el navegador, las APIs usadas y la lectura accesible de tu componente.