// html · Web Platform Advent #5
Web Components: Custom Elements, Shadow DOM y plantillas
Crea elementos HTML reutilizables con las API nativas de Web Components — define un Custom Element, encapsula estilos con el Shadow DOM, clona un <template> y pasa datos por atributos.
Los Web Components son un conjunto de API nativas del navegador que te permiten crear tus propios elementos HTML reutilizables — sin ningún framework. Un componente que publicas funciona igual en HTML puro, React, Vue o Svelte, porque es simplemente un elemento que el navegador entiende. Tres API lo hacen posible: los Custom Elements, el Shadow DOM y la etiqueta <template>.
Custom Elements
Un Custom Element es una clase que extiende HTMLElement, registrada con un nombre de etiqueta que debe contener un guion (para no colisionar nunca con una futura etiqueta nativa):
class GreetingCard extends HTMLElement {
connectedCallback() {
const p = document.createElement('p');
p.textContent = '¡Hola desde un custom element!';
this.append(p);
}
}
customElements.define('greeting-card', GreetingCard); Una vez definido, lo usas como cualquier otra etiqueta:
<greeting-card></greeting-card> La clase te ofrece callbacks de ciclo de vida. Los más útiles son connectedCallback (el elemento se añadió a la página), disconnectedCallback (se eliminó) y attributeChangedCallback (un atributo observado cambió).
Reaccionar a los atributos
Para pasar datos, lee atributos — igual que src en una imagen. Declara cuáles observar con un getter estático observedAttributes y luego responde en attributeChangedCallback. Construir el nodo con textContent mantiene seguros los valores que aporta el usuario:
class GreetingCard extends HTMLElement {
static get observedAttributes() {
return ['name'];
}
attributeChangedCallback(attr, oldVal, newVal) {
if (attr === 'name') this.render();
}
connectedCallback() {
this.render();
}
render() {
const name = this.getAttribute('name') || 'amigo';
this.replaceChildren();
const p = document.createElement('p');
p.textContent = '¡Hola, ' + name + '!';
this.append(p);
}
}
customElements.define('greeting-card', GreetingCard); Ahora <greeting-card name="Ada"></greeting-card> muestra «¡Hola, Ada!», y cambiar el atributo en vivo lo vuelve a renderizar.
El Shadow DOM: encapsulación de verdad
El Shadow DOM le da a un elemento un subárbol privado cuyos estilos y marcado están aislados del resto de la página. Un <style> dentro nunca puede filtrarse, y el CSS exterior nunca puede entrar por accidente. Constrúyelo con la API del DOM y un <slot> para el contenido proyectado:
class FancyButton extends HTMLElement {
connectedCallback() {
const shadow = this.attachShadow({ mode: 'open' });
const style = document.createElement('style');
style.textContent =
'button { background:#2563eb; color:#fff; border:0;' +
' border-radius:8px; padding:.6rem 1rem; }';
const button = document.createElement('button');
button.append(document.createElement('slot'));
shadow.append(style, button);
}
}
customElements.define('fancy-button', FancyButton); El <slot> es un marcador de posición: lo que pongas entre las etiquetas — <fancy-button>Guardar</fancy-button> — se proyecta en ese punto, así que el componente envuelve tu contenido sin reemplazarlo.
Plantillas: clonar una vez, reutilizar barato
El elemento <template> contiene marcado inerte, analizado pero no renderizado hasta que lo clonas. Es la forma eficiente de repetir una estructura:
<template id="row-tpl">
<li><span class="label"></span></li>
</template>
<script>
const tpl = document.getElementById('row-tpl');
const node = tpl.content.cloneNode(true);
node.querySelector('.label').textContent = 'Elemento 1';
document.querySelector('ul').append(node);
</script> Las tres piezas juntas
| API | Lo que aporta |
|---|---|
| Custom Elements | Tus propias etiquetas con callbacks de ciclo de vida |
| Shadow DOM | Encapsulación de estilo y marcado, slots |
<template> | Marcado inerte reutilizable y clonable |
Rara vez necesitas las tres a la vez. Un simple contenedor puede usar solo un Custom Element; un widget con estilo y autónomo combina un Custom Element con el Shadow DOM y un slot. Como el resultado es DOM estándar, encaja en cualquier stack y sobrevive al framework que estuviera de moda el año en que lo escribiste.