W3docs

Web Components

Aprenda Web Components do zero: elementos customizados, Shadow DOM, templates HTML, slots e callbacks de ciclo de vida, com exemplos práticos.

Web components permitem criar elementos HTML reutilizáveis e encapsulados — tags personalizadas como <user-card> ou <star-rating> que carregam sua própria marcação, estilos e comportamento. Por serem construídos sobre APIs nativas do navegador, funcionam em qualquer framework (ou sem framework algum) e mantêm seus internos isolados do restante da página. Este guia abrange os três blocos de construção dos web components, percorre a criação de um do zero e termina com hooks de ciclo de vida e boas práticas.

Quando usar web components?

Recorra aos web components quando quiser um widget de UI autocontido que:

  • Funcione em qualquer lugar — o mesmo <my-widget> pode ser inserido em uma página React, Vue, HTML puro ou renderizada no servidor, sem etapa de build.
  • Permaneça isolado — seus estilos não vazam para fora e os estilos da página não vazam para dentro, graças ao Shadow DOM.
  • Seja distribuído em um único arquivo — marcação, estilo e lógica vivem juntos, tornando o componente fácil de distribuir e reutilizar.

Design systems, widgets incorporáveis e bibliotecas de componentes compartilhadas são os casos de uso mais comuns.

Introdução aos Web Components

Web components são um conjunto de APIs da plataforma web que permitem criar novas tags HTML personalizadas, reutilizáveis e encapsuladas. Três tecnologias trabalham juntas:

  • Custom Elements — define novas tags HTML e seu comportamento.
  • Shadow DOM — encapsula a marcação e os estilos de um componente para que fiquem com escopo no elemento.
  • HTML Templates — declara partes reutilizáveis de marcação inerte que você instancia em tempo de execução.

Custom Elements

Custom elements definem novas tags HTML e seu comportamento. Eles se baseiam em classes JavaScript: um custom element é uma classe que estende HTMLElement. Uma vez registrada, a tag pode ser usada como qualquer elemento HTML padrão.

<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 trecho de código demonstra como criar um elemento HTML personalizado chamado my-element. A classe MyElement estende HTMLElement, anexa um shadow DOM para encapsular seu conteúdo e insere um parágrafo simples com o texto "Hello, World!".

Shadow DOM

O Shadow DOM fornece encapsulamento para seus custom elements. Ele permite anexar uma árvore DOM separada e oculta (a shadow tree) a um elemento, mantendo seus estilos e marcação com escopo no próprio elemento. Para um aprofundamento, veja o capítulo dedicado ao Shadow DOM 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>

Neste exemplo, a classe ShadowElement estende HTMLElement e anexa um shadow root ao elemento. O parâmetro mode: 'open' especifica que o Shadow DOM é acessível via JavaScript. Quando mode está definido como 'open', o shadow root pode ser acessado usando a propriedade element.shadowRoot. Se mode estiver definido como 'closed', o shadow root não é acessível externamente, reforçando o encapsulamento e impedindo que scripts externos manipulem o Shadow DOM diretamente.

Dentro do Shadow DOM, definimos alguns estilos e conteúdo HTML. Os estilos (neste caso, um parágrafo estilizado com texto azul) têm escopo no Shadow DOM, garantindo que não afetem outros elementos da página. Esse encapsulamento é benéfico para criar componentes reutilizáveis com estilo e comportamento consistentes, independentemente do contexto em que são usados.

Aviso

Use estilos locais dentro do Shadow DOM para evitar vazamento de estilos e garantir que os estilos do componente tenham escopo.

Para mais informações sobre escopo de CSS em uma shadow tree — incluindo :host e propriedades customizadas CSS que podem cruzar a fronteira — veja Estilização do Shadow DOM.

HTML Templates

Os HTML Templates permitem definir partes reutilizáveis de HTML que são inertes — são analisadas, mas não renderizadas, e seus scripts não são executados — até você cloná-las e inseri-las em tempo de execução. O elemento <template> é a forma padrão de fazer isso.

<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 exemplo mostra como usar HTML Templates. A tag <template> contém HTML e estilos que não são renderizados imediatamente. O script clona o conteúdo do template e o anexa ao corpo do documento, tornando-o visível.

Criando Seu Primeiro Web Component

Vamos percorrer o processo de criação de um web component totalmente funcional que pode ser usado em diferentes projetos.

Passo 1: Definir o Componente

Crie uma nova classe que estende 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 trecho estabelece a base para um web component chamado my-component. Ele define uma classe que estende HTMLElement e anexa um Shadow DOM, preparando-o para personalização adicional.

Passo 2: Adicionar Estilos e Templates

Use o Shadow DOM para encapsular estilos e templates.

<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 trecho aprimora my-styled-component adicionando estilos com escopo dentro do Shadow DOM. O parágrafo dentro do componente é estilizado com cor verde e tamanho de fonte aumentado.

Passo 3: Adicionar Interatividade

Adicione JavaScript para tornar seu componente interativo.

<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 exemplo mostra como tornar um web component interativo. O InteractiveComponent inclui um botão que, ao ser clicado, altera o conteúdo de texto de um parágrafo dentro do componente.

Técnicas Avançadas de Web Components

Callbacks de Ciclo de Vida

Custom elements possuem callbacks de ciclo de vida que permitem executar código durante fases específicas da vida de um 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 trecho demonstra o uso de callbacks de ciclo de vida em web components. O LifecycleComponent atualiza seu conteúdo quando adicionado à página e registra uma mensagem quando removido.

Tratamento de Mudanças de Atributo

Reaja a mudanças nos atributos de um elemento definindo o array observedAttributes e implementando o 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 exemplo mostra como lidar com mudanças de atributo em um web component. O AttributeComponent atualiza seu conteúdo interno com base nas mudanças no atributo data-text. Observe que attributeChangedCallback só é acionado para atributos listados em observedAttributes — sem essa lista, as mudanças de atributo são ignoradas.

Os quatro callbacks de ciclo de vida de custom elements são:

CallbackQuando é executado
constructor()Quando o elemento é criado ou atualizado. Configure o estado inicial aqui, mas evite tocar em atributos ou DOM filho.
connectedCallback()Cada vez que o elemento é inserido no documento. Melhor lugar para renderizar e adicionar event listeners.
disconnectedCallback()Cada vez que o elemento é removido do documento. Limpe listeners e timers aqui.
attributeChangedCallback(name, oldVal, newVal)Quando um atributo observado muda. Requer um getter estático observedAttributes.

Distribuindo Filhos com Slots

Um <slot> permite que um componente renderize a marcação que o usuário coloca entre suas tags, para que os consumidores possam passar seu próprio conteúdo. Esta é a base da composição com web components — veja Shadow DOM, slots e composição para o panorama completo.

<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>

O primeiro <card-box> renderiza "Passed-in content" dentro da caixa estilizada; o segundo recorre ao conteúdo padrão do slot "Default content".

Boas Práticas para Web Components

Use o Shadow DOM com Sabedoria

Encapsule estilos e scripts dentro do Shadow DOM para evitar conflitos com outros elementos da página.

Siga as Convenções de Nomenclatura

Custom elements devem conter um hífen em seu nome (por exemplo, my-card, não mycard) para evitar conflitos com elementos HTML padrão atuais e futuros. Registrar um nome sem hífen gera um erro.

Mantenha os Componentes Modulares

Crie componentes pequenos e reutilizáveis para manter sua base de código sustentável e escalável.

Evite Estilos Globais

Use estilos locais dentro do Shadow DOM para evitar vazamento de estilos e garantir que os estilos do componente tenham escopo.

Documentação e Testes

Documente bem seus componentes e escreva testes para garantir que funcionem conforme esperado em diferentes navegadores e casos de uso.

Conclusão

Web components são um recurso versátil e poderoso no desenvolvimento web moderno, permitindo a criação de elementos customizados reutilizáveis e encapsulados. Ao compreender e utilizar Custom Elements, Shadow DOM e HTML Templates, você pode criar aplicações web modulares, sustentáveis e escaláveis. Este guia forneceu o conhecimento e os exemplos necessários para começar com web components, garantindo que você possa implementá-los efetivamente em seus projetos.

Para ir além, explore o Shadow DOM JavaScript, como Shadow DOM e eventos interagem, e o elemento <template> em profundidade.

Prática

Prática
Qual das seguintes afirmações sobre Web Components está correta?
Qual das seguintes afirmações sobre Web Components está correta?
Was this page helpful?