Web Components sempre tiveram um problema de renderização no servidor: o Shadow DOM só existia depois que o JavaScript rodava no navegador, então uma página renderizada no servidor chegava sem o conteúdo encapsulado, causando um "flash" de elemento vazio até o script assumir. O Declarative Shadow DOM resolve isso permitindo criar uma shadow root direto no HTML, usando um <template> com o atributo shadowrootmode, sem precisar de nenhuma linha de JavaScript.
O jeito antigo
class MeuCartao extends HTMLElement {
connectedCallback() {
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>p { color: darkblue; }</style>
<p>Conteúdo encapsulado</p>
`;
}
}
customElements.define('meu-cartao', MeuCartao);
Isso funciona, mas o conteúdo dentro do shadow só aparece depois que esse script executa. No servidor, o HTML enviado é só <meu-cartao></meu-cartao> vazio — ruim para SEO, para o primeiro paint e para quem desabilitou JavaScript.
Como funciona o shadowrootmode
Um <template> com o atributo shadowrootmode dentro de um elemento é interpretado pelo próprio parser HTML como a declaração de uma shadow root, sem esperar nenhum script:
<meu-cartao>
<template shadowrootmode="open">
<style>p { color: darkblue; }</style>
<p>Conteúdo encapsulado</p>
</template>
</meu-cartao>
Assim que o navegador encontra esse HTML, ele já cria a shadow root e move o conteúdo do <template> para dentro dela — o CSS do <style> fica isolado do resto da página, e o parágrafo aparece imediatamente, sem esperar JavaScript nenhum.
open vs closed
O valor de shadowrootmode segue as mesmas regras do mode usado em attachShadow():
<meu-cartao>
<template shadowrootmode="closed">
<p>Esse conteúdo não é acessível via elemento.shadowRoot</p>
</template>
</meu-cartao>
Com open, o JavaScript da página consegue acessar a shadow root normalmente por elemento.shadowRoot. Com closed, essa propriedade retorna null, mantendo o conteúdo realmente isolado até de scripts externos.
Combinando com Custom Elements
O elemento customizado ainda precisa ser definido em JavaScript para ganhar comportamento, mas o conteúdo visual já existe antes disso acontecer:
<contador-clicks>
<template shadowrootmode="open">
<style>button { padding: 8px 16px; }</style>
<button>Cliques: <span>0</span></button>
</template>
</contador-clicks>
customElements.define('contador-clicks', class extends HTMLElement {
connectedCallback() {
const span = this.shadowRoot.querySelector('span');
this.shadowRoot.querySelector('button').addEventListener('click', () => {
span.textContent = Number(span.textContent) + 1;
});
}
});
Como a shadow root já existe quando connectedCallback() roda, o código só precisa buscar os elementos e adicionar comportamento — não montar a estrutura do zero.
Verificando suporte em runtime
Para checar se o navegador entende o atributo antes de depender dele:
const suportaDSD = HTMLTemplateElement.prototype.hasOwnProperty('shadowRootMode');
if (!suportaDSD) {
// aplicar um polyfill que converte os templates manualmente em shadow roots
}
Quando usar
- Componentes renderizados no servidor (SSR) que usam Shadow DOM e precisam aparecer completos no HTML inicial.
- Bibliotecas e frameworks de Web Components que geram HTML estático (build time ou server-side).
- Melhorar SEO e o First Contentful Paint de páginas que dependem de componentes encapsulados.
- Formulários e widgets que precisam funcionar mesmo antes do JavaScript da página terminar de carregar.
Cuidados
Um <template shadowrootmode> só é interpretado dessa forma quando está diretamente dentro do elemento que vai hospedar a shadow root, no momento em que o parser HTML lê o documento — inseri-lo depois via innerHTML não cria a shadow root automaticamente, sendo necessário usar a API parseHTMLUnsafe() ou montá-la manualmente. Cada elemento também só pode ter uma shadow root declarada dessa forma.
Compatibilidade
O suporte já está presente em todos os navegadores principais, incluindo Safari, que foi o último a implementar. Ainda assim, vale checar a compatibilidade antes de depender disso como única forma de renderizar um componente.
Conclusão
O Declarative Shadow DOM tira o Shadow DOM da dependência exclusiva de JavaScript, permitindo que Web Components apareçam completos e estilizados assim que o HTML é carregado — um passo importante para tornar essa tecnologia viável em cenários de renderização no servidor.
0 Comentário
Deixe seu Comentário!