Declarative Shadow DOM

Como criar uma shadow root direto no HTML usando <template shadowrootmode>, sem JavaScript, viabilizando Web Components renderizados no servidor.

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

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.

caniuse

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.

Tecnologia:
html

Deixe seu Comentário!

Comentário
Nome
Email