W3docs

Web Components

Aprende Web Components en JavaScript: elementos personalizados, Shadow DOM, plantillas HTML, slots y callbacks de ciclo de vida, con ejemplos y buenas prácticas.

Los Web Components te permiten crear tus propios elementos HTML reutilizables y encapsulados — etiquetas personalizadas como <user-card> o <star-rating> que llevan su propio marcado, estilos y comportamiento. Como se construyen sobre APIs nativas del navegador, funcionan en cualquier framework (o sin framework alguno) y mantienen sus partes internas aisladas del resto de la página. Esta guía cubre los tres bloques de construcción de los Web Components, recorre el proceso de crear uno desde cero y termina con los hooks del ciclo de vida y las buenas prácticas.

¿Cuándo usaría Web Components?

Recurre a los Web Components cuando necesites un widget de interfaz de usuario autocontenido que:

  • Funcione en todas partes — el mismo <my-widget> se puede insertar en una página de React, Vue, HTML plano o renderizada en el servidor sin ningún paso de compilación.
  • Permanezca aislado — sus estilos no pueden escapar hacia afuera y los estilos de la página no pueden entrar, gracias al Shadow DOM.
  • Se distribuya como un solo archivo — el marcado, el estilo y la lógica conviven juntos, lo que facilita distribuir y reutilizar el componente.

Los sistemas de diseño, los widgets embebibles y las bibliotecas de componentes compartidos son los casos de uso más habituales.

Introducción a los Web Components

Los Web Components son un conjunto de APIs de la plataforma web que permiten crear nuevas etiquetas HTML personalizadas, reutilizables y encapsuladas. Tres tecnologías trabajan juntas:

  • Custom Elements — definen nuevas etiquetas HTML y su comportamiento.
  • Shadow DOM — encapsula el marcado y los estilos de un componente para que queden limitados al elemento.
  • HTML Templates — declaran fragmentos reutilizables de marcado inerte que se instancian en tiempo de ejecución.

Custom Elements

Los Custom Elements definen nuevas etiquetas HTML y su comportamiento. Se basan en las clases de JavaScript: un Custom Element es una clase que extiende HTMLElement. Una vez registrada, la etiqueta puede usarse como cualquier elemento HTML estándar.

<div>
  <script>
    class MyElement extends HTMLElement {
      constructor() {
        super();
        this.attachShadow({ mode: 'open' });
        this.shadowRoot.innerHTML = '<p>Hello, World!</p>';
      }
    }
    customElements.define('my-element', MyElement);
  </script>
  <my-element></my-element>
</div>

Este fragmento de código muestra cómo crear un elemento HTML personalizado llamado my-element. La clase MyElement extiende HTMLElement, adjunta un Shadow DOM para encapsular su contenido e inserta un párrafo sencillo con el texto "Hello, World!".

Shadow DOM

El Shadow DOM proporciona encapsulación para tus Custom Elements. Permite adjuntar un árbol DOM separado y oculto (el shadow tree) a un elemento, manteniendo sus estilos y marcado limitados al propio elemento. Para un análisis más profundo, consulta el capítulo dedicado al Shadow DOM de JavaScript.

<div>
  <script>
    class ShadowElement extends HTMLElement {
      constructor() {
        super();
        let shadow = this.attachShadow({ mode: 'open' });
        shadow.innerHTML = `
          <style>
            p { color: blue; }
          </style>
          <p>Shadow DOM content</p>
        `;
      }
    }
    customElements.define('shadow-element', ShadowElement);
  </script>
  <shadow-element></shadow-element>
</div>

En este ejemplo, la clase ShadowElement extiende HTMLElement y adjunta una shadow root al elemento. El parámetro mode: 'open' especifica que el Shadow DOM es accesible mediante JavaScript. Cuando mode se establece en 'open', se puede acceder a la shadow root mediante la propiedad element.shadowRoot. Si mode se establece en 'closed', la shadow root no es accesible desde el exterior, lo que mejora la encapsulación e impide que scripts externos manipulen el Shadow DOM directamente.

Dentro del Shadow DOM, definimos algunos estilos y contenido HTML. Los estilos (en este caso, un párrafo con texto azul) están limitados al Shadow DOM, lo que garantiza que no afecten a ningún otro elemento de la página. Esta encapsulación resulta beneficiosa para crear componentes reutilizables con estilos y comportamiento coherentes, independientemente del contexto en que se utilicen.

Advertencia

Utiliza estilos locales dentro del Shadow DOM para evitar la fuga de estilos y garantizar que los estilos del componente queden limitados a él.

Para más información sobre cómo limitar CSS a un shadow tree — incluidos :host y las propiedades personalizadas de CSS que pueden cruzar el límite — consulta Shadow DOM styling.

HTML Templates

Las HTML Templates permiten definir fragmentos reutilizables de HTML que son inertes — se analizan pero no se renderizan, y sus scripts no se ejecutan — hasta que los clonas e insertas en tiempo de ejecución. El elemento <template> es la forma estándar de hacerlo.

<div>
  <template id="my-template">
    <style>
      p { color: red; }
    </style>
    <p>This is a template content</p>
  </template>
  <script>
    const template = document.getElementById('my-template').content;
    document.body.appendChild(template.cloneNode(true));
  </script>
</div>

Este ejemplo muestra cómo usar HTML Templates. La etiqueta <template> contiene HTML y estilos que no se renderizan de inmediato. El script clona el contenido de la plantilla y lo añade al cuerpo del documento, haciéndolo visible.

Crea tu primer Web Component

Recorramos el proceso de crear un Web Component completamente funcional que pueda usarse en diferentes proyectos.

Paso 1: Define el componente

Crea una nueva clase que extienda HTMLElement.

<div>
  <script>
    class MyComponent extends HTMLElement {
      constructor() {
        super();
        const shadow = this.attachShadow({ mode: 'open' });
        shadow.innerHTML = 'Hello, World!';
      }
    }
    customElements.define('my-component', MyComponent);
  </script>
  <my-component></my-component>
</div>

Este fragmento establece la base de un Web Component llamado my-component. Define una clase que extiende HTMLElement y adjunta un Shadow DOM, preparándolo para una mayor personalización.

Paso 2: Añade estilos y plantillas

Usa el Shadow DOM para encapsular estilos y plantillas.

<div>
  <script>
    class MyStyledComponent extends HTMLElement {
      constructor() {
        super();
        let shadow = this.attachShadow({ mode: 'open' });
        shadow.innerHTML = `
          <style>
            p { font-size: 20px; color: green; }
          </style>
          <p>This is my styled component!</p>
        `;
      }
    }
    customElements.define('my-styled-component', MyStyledComponent);
  </script>
  <my-styled-component></my-styled-component>
</div>

Este fragmento mejora my-styled-component añadiendo estilos limitados dentro del Shadow DOM. El párrafo dentro del componente se estiliza con color verde y un tamaño de fuente mayor.

Paso 3: Añade interactividad

Añade JavaScript para que tu componente sea interactivo.

<div>
  <script>
    class InteractiveComponent extends HTMLElement {
      constructor() {
        super();
        this.attachShadow({ mode: 'open' });
        this.shadowRoot.innerHTML = `
          <style>
            p { font-size: 20px; }
          </style>
          <p>This is interactive!</p>
          <button>Click me</button>
        `;
        this.shadowRoot.querySelector('button').addEventListener('click', () => {
          this.shadowRoot.querySelector('p').textContent = 'You clicked!';
        });
      }
    }
    customElements.define('interactive-component', InteractiveComponent);
  </script>
  <interactive-component></interactive-component>
</div>

Este ejemplo muestra cómo hacer interactivo un Web Component. El InteractiveComponent incluye un botón que, al hacer clic, cambia el contenido de texto de un párrafo dentro del componente.

Técnicas avanzadas de Web Components

Callbacks del ciclo de vida

Los Custom Elements disponen de callbacks del ciclo de vida que permiten ejecutar código durante fases específicas de la vida de un elemento.

<div>
  <script>
    class LifecycleComponent extends HTMLElement {
      constructor() {
        super();
        this.attachShadow({ mode: 'open' });
      }
      connectedCallback() {
        this.shadowRoot.innerHTML = '<p>Element added to page.</p>';
      }
      disconnectedCallback() {
        console.log('Element removed from page.');
      }
    }
    customElements.define('lifecycle-component', LifecycleComponent);
  </script>
  <lifecycle-component></lifecycle-component>
</div>

Este fragmento muestra el uso de callbacks del ciclo de vida en Web Components. El LifecycleComponent actualiza su contenido cuando se añade a la página y registra un mensaje cuando se elimina.

Manejo de cambios en atributos

Reacciona a los cambios en los atributos de un elemento definiendo el array observedAttributes e implementando el método attributeChangedCallback.

<div>
  <script>
    class AttributeComponent extends HTMLElement {
      static get observedAttributes() {
        return ['data-text'];
      }
      constructor() {
        super();
        this.attachShadow({ mode: 'open' });
        this.shadowRoot.innerHTML = '<p></p>';
      }
      attributeChangedCallback(name, oldValue, newValue) {
        if (name === 'data-text') {
          this.shadowRoot.querySelector('p').textContent = newValue;
        }
      }
    }
    customElements.define('attribute-component', AttributeComponent);
  </script>
  <attribute-component data-text="Initial text"></attribute-component>
  <script>
    const element = document.querySelector('attribute-component');
    setTimeout(() => {
      element.setAttribute('data-text', 'Updated text');
    }, 2000);
  </script>
</div>

Este ejemplo muestra cómo manejar cambios de atributos en un Web Component. El AttributeComponent actualiza su contenido interno en función de los cambios en el atributo data-text. Ten en cuenta que attributeChangedCallback solo se activa para los atributos listados en observedAttributes — sin esa lista, los cambios de atributos se ignoran.

Los cuatro callbacks del ciclo de vida de los Custom Elements son:

CallbackCuándo se ejecuta
constructor()Cuando el elemento se crea o actualiza. Configura el estado inicial aquí, pero evita tocar atributos o el DOM hijo.
connectedCallback()Cada vez que el elemento se inserta en el documento. El mejor lugar para renderizar y añadir listeners de eventos.
disconnectedCallback()Cada vez que el elemento se elimina del documento. Limpia listeners y temporizadores aquí.
attributeChangedCallback(name, oldVal, newVal)Cuando un atributo observado cambia. Requiere un getter estático observedAttributes.

Distribución de hijos con slots

Un <slot> permite a un componente renderizar el marcado que el usuario coloca entre sus etiquetas, de modo que los consumidores puedan pasar su propio contenido. Esta es la base de la composición con Web Components — consulta Shadow DOM, slots y composición para una visión completa.

<div>
  <script>
    class CardBox extends HTMLElement {
      constructor() {
        super();
        this.attachShadow({ mode: 'open' });
        this.shadowRoot.innerHTML = `
          <style>
            .box { border: 1px solid #ccc; padding: 8px; }
          </style>
          <div class="box"><slot>Default content</slot></div>
        `;
      }
    }
    customElements.define('card-box', CardBox);
  </script>
  <card-box>Passed-in content</card-box>
  <card-box></card-box>
</div>

El primer <card-box> renderiza "Passed-in content" dentro del cuadro con estilo; el segundo recurre al contenido predeterminado del slot "Default content".

Buenas prácticas para Web Components

Usa el Shadow DOM con criterio

Encapsula los estilos y scripts dentro del Shadow DOM para evitar conflictos con otros elementos de la página.

Sigue las convenciones de nomenclatura

Los Custom Elements deben contener un guión en su nombre (por ejemplo, my-card, no mycard) para evitar conflictos con los elementos HTML estándar actuales y futuros. Registrar un nombre sin guión lanza un error.

Mantén los componentes modulares

Crea componentes pequeños y reutilizables para mantener tu base de código manejable y escalable.

Evita los estilos globales

Usa estilos locales dentro del Shadow DOM para evitar la fuga de estilos y garantizar que los estilos del componente queden limitados a él.

Documentación y pruebas

Documenta bien tus componentes y escribe pruebas para asegurarte de que funcionan correctamente en diferentes navegadores y casos de uso.

Conclusión

Los Web Components son una característica versátil y poderosa del desarrollo web moderno que permite crear elementos personalizados reutilizables y encapsulados. Al comprender y utilizar los Custom Elements, el Shadow DOM y las HTML Templates, puedes crear aplicaciones web modulares, mantenibles y escalables. Esta guía te ha proporcionado el conocimiento y los ejemplos necesarios para comenzar con los Web Components, asegurándote de que puedas implementarlos de manera efectiva en tus proyectos.

Para ir más lejos, explora el Shadow DOM de JavaScript, cómo interactúan el Shadow DOM y los eventos, y el elemento <template> en profundidad.

Práctica

Práctica
¿Cuál de las siguientes afirmaciones sobre los Web Components es correcta?
¿Cuál de las siguientes afirmaciones sobre los Web Components es correcta?
Was this page helpful?