Construyendo Componentes Web Flexibles con Slots

Los componentes web son poderosos, pero pasar contenido complejo a ellos puede volverse rápidamente desordenado. Imagina intentar construir un componente de tarjeta que necesita una imagen de encabezado, título, texto del cuerpo y botones de acción—meter todo eso en atributos crearía un desastre ilegible. Ahí es donde entran los slots, transformando cómo construimos componentes de UI flexibles y reutilizables.
Este artículo te muestra cómo usar slots para crear componentes web que acepten contenido rico mientras mantienen un marcado limpio y declarativo. Aprenderás cómo funcionan los slots con shadow DOM, cómo implementar tanto slots por defecto como nombrados, y cómo aplicar estilos al contenido slotted de manera efectiva.
Puntos Clave
- Los slots permiten que los componentes web acepten contenido HTML complejo en lugar de meter todo en atributos
- Los slots nombrados proporcionan control preciso sobre la colocación del contenido, mientras que los slots por defecto manejan contenido no especificado
- El pseudo-elemento
::slotted()
permite aplicar estilos al contenido slotted desde dentro del shadow DOM - Los slots mantienen excelente rendimiento al proyectar nodos DOM en lugar de copiarlos
El Problema: Contenido Complejo en Componentes Web
Los atributos HTML tradicionales funcionan bien para valores simples:
<user-avatar src="profile.jpg" size="large"></user-avatar>
Pero ¿qué pasa cuando necesitas pasar contenido estructurado? Considera un componente de tarjeta:
<!-- Esto se vuelve desordenado rápidamente -->
<product-card
title="Premium Headphones"
description="<p>Audio de alta calidad con <strong>cancelación de ruido</strong></p>"
price="$299"
button-text="Agregar al Carrito"
image-src="headphones.jpg">
</product-card>
Este enfoque tiene varios problemas:
- El HTML dentro de atributos requiere escape
- Los layouts complejos se vuelven imposibles de manejar
- El uso del componente no coincide con los patrones estándar de HTML
Cómo Funcionan los Slots: Lo Básico
Los slots te permiten pasar contenido directamente entre las etiquetas de tu componente, igual que los elementos HTML nativos. Así es como transforman el ejemplo anterior:
<product-card>
<h2 slot="title">Premium Headphones</h2>
<div slot="description">
<p>Audio de alta calidad con <strong>cancelación de ruido</strong></p>
</div>
<button slot="action">Agregar al Carrito</button>
</product-card>
Dentro de tu componente web, defines dónde aparece este contenido usando el elemento <slot>
:
class ProductCard extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
.card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 16px;
}
</style>
<div class="card">
<slot name="title">Producto Sin Título</slot>
<slot name="description">No hay descripción disponible</slot>
<slot name="action"></slot>
</div>
`;
}
}
customElements.define('product-card', ProductCard);
El contenido dentro de cada etiqueta <slot>
sirve como contenido de respaldo—aparece cuando no se proporciona contenido slotted coincidente.
Slots Por Defecto vs Slots Nombrados
Los componentes web soportan dos tipos de slots:
Slots Por Defecto
Cualquier contenido sin un atributo slot
va al slot por defecto (sin nombre):
// Definición del componente
shadow.innerHTML = `
<article>
<h2>Título del Artículo</h2>
<slot></slot> <!-- Slot por defecto -->
</article>
`;
<!-- Uso -->
<my-article>
<p>Este párrafo va al slot por defecto</p>
<p>Este también</p>
</my-article>
Slots Nombrados
Los slots nombrados te dan control preciso sobre la colocación del contenido:
// Definición del componente
shadow.innerHTML = `
<div class="profile">
<slot name="avatar"></slot>
<div class="info">
<slot name="name">Anónimo</slot>
<slot name="bio">No hay biografía proporcionada</slot>
</div>
</div>
`;
<!-- Uso -->
<user-profile>
<img slot="avatar" src="jane.jpg" alt="Jane">
<h3 slot="name">Jane Developer</h3>
<p slot="bio">Construyendo componentes web increíbles</p>
</user-profile>
Ejemplo del Mundo Real: Construyendo un Componente de Tarjeta Flexible
Construyamos un componente de tarjeta listo para producción que demuestre los slots en acción:
class FlexCard extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
:host {
display: block;
border: 1px solid #e0e0e0;
border-radius: 8px;
overflow: hidden;
background: white;
}
.header {
padding: 16px;
border-bottom: 1px solid #e0e0e0;
}
.content {
padding: 16px;
}
.footer {
padding: 16px;
background: #f5f5f5;
}
/* Ocultar secciones vacías cuando no hay contenido slotted */
.header:empty {
display: none;
}
.footer:empty {
display: none;
}
</style>
<div class="header">
<slot name="header"></slot>
</div>
<div class="content">
<slot></slot>
</div>
<div class="footer">
<slot name="footer"></slot>
</div>
`;
}
}
customElements.define('flex-card', FlexCard);
Ahora puedes usarlo con cualquier estructura de contenido:
<flex-card>
<h2 slot="header">Detalles del Producto</h2>
<p>El contenido principal va en el slot por defecto</p>
<ul>
<li>Característica 1</li>
<li>Característica 2</li>
</ul>
<div slot="footer">
<button>Comprar Ahora</button>
<button>Guardar para Después</button>
</div>
</flex-card>
Aplicando Estilos al Contenido Slotted
Aplicar estilos al contenido slotted requiere pseudo-elementos especiales:
Usando ::slotted()
El pseudo-elemento ::slotted()
apunta a elementos colocados en slots:
/* Dentro del shadow DOM de tu componente */
::slotted(h2) {
color: #333;
margin: 0;
}
::slotted(button) {
background: #007bff;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
}
/* Apuntar a slots específicos */
::slotted([slot="header"]) {
font-size: 1.2em;
}
Limitación importante: ::slotted()
solo apunta al elemento slotted directo, no a sus hijos.
Usando :host
La pseudo-clase :host
aplica estilos al componente mismo:
:host {
display: block;
margin: 16px 0;
}
/* Aplicar estilos basados en atributos */
:host([variant="primary"]) {
border-color: #007bff;
}
/* Aplicar estilos basados en contexto */
:host-context(.dark-theme) {
background: #333;
color: white;
}
Consideraciones de Rendimiento
Los slots son altamente eficientes porque no copian nodos DOM—los proyectan. El contenido slotted permanece en el light DOM pero se renderiza como si fuera parte del shadow DOM. Esto significa:
- Los event listeners en el contenido slotted continúan funcionando
- Los estilos del documento aún pueden aplicarse (a menos que sean bloqueados por el shadow DOM)
- El navegador no duplica nodos en memoria
Soporte de Navegadores y Polyfills
Los slots de componentes web tienen excelente soporte en navegadores modernos. Para navegadores más antiguos, considera usar los polyfills de Web Components.
Conclusión
Los slots transforman los componentes web de elementos personalizados simples en bloques de construcción poderosos y flexibles para UI reutilizable. Al separar estructura de contenido, te permiten crear componentes que son tanto altamente personalizables como fáciles de usar. Ya sea que estés construyendo un sistema de diseño u organizando mejor tu código, dominar los slots es esencial para el desarrollo moderno de componentes web.
¿Listo para crear componentes web más flexibles? Comienza refactorizando uno de tus componentes existentes para usar slots. Enfócate en áreas donde actualmente estés pasando HTML a través de atributos o usando estructuras de props complejas. Tu yo futuro (y tu equipo) te agradecerán por el código más limpio y mantenible.
Preguntas Frecuentes
Los props (atributos) son mejores para valores simples como strings, números o booleanos. Los slots destacan en aceptar contenido HTML complejo, múltiples elementos o cualquier estructura de marcado. Usa props para configuración y slots para contenido.
Sí, puedes modificar el contenido slotted en cualquier momento ya que permanece en el light DOM. Simplemente selecciona los elementos con atributos slot y actualízalos como cualquier otro elemento DOM. Los cambios se reflejan inmediatamente en el componente renderizado.
El contenido slotted permanece en el light DOM, haciéndolo completamente accesible para motores de búsqueda y lectores de pantalla. Esta es una ventaja importante sobre el contenido del shadow DOM, que puede ser más difícil de indexar para los crawlers.
Todos los elementos con el mismo nombre de slot aparecen en ese slot en orden del documento. Esto es útil para crear layouts flexibles donde los usuarios pueden agregar múltiples elementos a una sola área de slot.
No, los slots requieren shadow DOM para funcionar. Están específicamente diseñados para proyectar contenido del light DOM en plantillas del shadow DOM. Sin shadow DOM, necesitarías usar diferentes patrones de distribución de contenido.