Construindo Web Components Flexíveis com Slots

Web components são poderosos, mas passar conteúdo complexo para eles pode rapidamente se tornar confuso. Imagine tentar construir um componente de card que precisa de uma imagem de cabeçalho, título, texto do corpo e botões de ação—colocar tudo isso em atributos criaria uma bagunça ilegível. É aí que os slots entram em cena, transformando como construímos componentes de UI flexíveis e reutilizáveis.
Este artigo mostra como usar slots para criar web components que aceitam conteúdo rico mantendo uma marcação limpa e declarativa. Você aprenderá como os slots funcionam com shadow DOM, como implementar tanto slots padrão quanto nomeados, e como estilizar conteúdo slotted efetivamente.
Principais Pontos
- Slots permitem que web components aceitem conteúdo HTML complexo em vez de colocar tudo em atributos
- Slots nomeados fornecem controle preciso sobre o posicionamento do conteúdo, enquanto slots padrão lidam com conteúdo não especificado
- O pseudo-elemento
::slotted()
permite estilizar conteúdo slotted de dentro do shadow DOM - Slots mantêm excelente performance ao projetar nós DOM em vez de copiá-los
O Problema: Conteúdo Complexo em Web Components
Atributos HTML tradicionais funcionam bem para valores simples:
<user-avatar src="profile.jpg" size="large"></user-avatar>
Mas o que acontece quando você precisa passar conteúdo estruturado? Considere um componente de card:
<!-- Isso fica confuso rapidamente -->
<product-card
title="Premium Headphones"
description="<p>Áudio de alta qualidade com <strong>cancelamento de ruído</strong></p>"
price="$299"
button-text="Adicionar ao Carrinho"
image-src="headphones.jpg">
</product-card>
Esta abordagem tem vários problemas:
- HTML dentro de atributos requer escape
- Layouts complexos se tornam impossíveis de gerenciar
- O uso do componente não corresponde aos padrões HTML padrão
Como os Slots Funcionam: O Básico
Slots permitem que você passe conteúdo diretamente entre as tags do seu componente, assim como elementos HTML nativos. Veja como eles transformam o exemplo anterior:
<product-card>
<h2 slot="title">Premium Headphones</h2>
<div slot="description">
<p>Áudio de alta qualidade com <strong>cancelamento de ruído</strong></p>
</div>
<button slot="action">Adicionar ao Carrinho</button>
</product-card>
Dentro do seu web component, você define onde este conteúdo aparece usando o elemento <slot>
:
class ProductCard extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
.card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 16px;
}
</style>
<div class="card">
<slot name="title">Produto Sem Título</slot>
<slot name="description">Nenhuma descrição disponível</slot>
<slot name="action"></slot>
</div>
`;
}
}
customElements.define('product-card', ProductCard);
O conteúdo dentro de cada tag <slot>
serve como conteúdo de fallback—ele aparece quando nenhum conteúdo slotted correspondente é fornecido.
Slots Padrão vs Nomeados
Web components suportam dois tipos de slots:
Slots Padrão
Qualquer conteúdo sem um atributo slot
vai para o slot padrão (sem nome):
// Definição do componente
shadow.innerHTML = `
<article>
<h2>Título do Artigo</h2>
<slot></slot> <!-- Slot padrão -->
</article>
`;
<!-- Uso -->
<my-article>
<p>Este parágrafo vai para o slot padrão</p>
<p>Este também</p>
</my-article>
Slots Nomeados
Slots nomeados dão controle preciso sobre o posicionamento do conteúdo:
// Definição do componente
shadow.innerHTML = `
<div class="profile">
<slot name="avatar"></slot>
<div class="info">
<slot name="name">Anônimo</slot>
<slot name="bio">Nenhuma bio fornecida</slot>
</div>
</div>
`;
<!-- Uso -->
<user-profile>
<img slot="avatar" src="jane.jpg" alt="Jane">
<h3 slot="name">Jane Developer</h3>
<p slot="bio">Construindo web components incríveis</p>
</user-profile>
Exemplo do Mundo Real: Construindo um Componente de Card Flexível
Vamos construir um componente de card pronto para produção que demonstra slots em ação:
class FlexCard extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
:host {
display: block;
border: 1px solid #e0e0e0;
border-radius: 8px;
overflow: hidden;
background: white;
}
.header {
padding: 16px;
border-bottom: 1px solid #e0e0e0;
}
.content {
padding: 16px;
}
.footer {
padding: 16px;
background: #f5f5f5;
}
/* Ocultar seções vazias quando não há conteúdo slotted */
.header:empty {
display: none;
}
.footer:empty {
display: none;
}
</style>
<div class="header">
<slot name="header"></slot>
</div>
<div class="content">
<slot></slot>
</div>
<div class="footer">
<slot name="footer"></slot>
</div>
`;
}
}
customElements.define('flex-card', FlexCard);
Agora você pode usá-lo com qualquer estrutura de conteúdo:
<flex-card>
<h2 slot="header">Detalhes do Produto</h2>
<p>O conteúdo principal vai no slot padrão</p>
<ul>
<li>Funcionalidade 1</li>
<li>Funcionalidade 2</li>
</ul>
<div slot="footer">
<button>Comprar Agora</button>
<button>Salvar para Depois</button>
</div>
</flex-card>
Estilizando Conteúdo Slotted
Estilizar conteúdo slotted requer pseudo-elementos especiais:
Usando ::slotted()
O pseudo-elemento ::slotted()
direciona elementos colocados em slots:
/* Dentro do shadow DOM do seu componente */
::slotted(h2) {
color: #333;
margin: 0;
}
::slotted(button) {
background: #007bff;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
}
/* Direcionar slots específicos */
::slotted([slot="header"]) {
font-size: 1.2em;
}
Limitação importante: ::slotted()
apenas direciona o elemento slotted direto, não seus filhos.
Usando :host
A pseudo-classe :host
estiliza o próprio componente:
:host {
display: block;
margin: 16px 0;
}
/* Estilizar baseado em atributos */
:host([variant="primary"]) {
border-color: #007bff;
}
/* Estilizar baseado no contexto */
:host-context(.dark-theme) {
background: #333;
color: white;
}
Considerações de Performance
Slots são altamente performáticos porque não copiam nós DOM—eles os projetam. O conteúdo slotted permanece no light DOM mas renderiza como se fosse parte do shadow DOM. Isso significa:
- Event listeners no conteúdo slotted continuam funcionando
- Estilos do documento ainda podem se aplicar (a menos que bloqueados pelo shadow DOM)
- O navegador não duplica nós na memória
Suporte de Navegadores e Polyfills
Slots de web components têm excelente suporte em navegadores modernos. Para navegadores mais antigos, considere usar os polyfills de Web Components.
Conclusão
Slots transformam web components de elementos customizados simples em blocos de construção poderosos e flexíveis para UI reutilizável. Ao separar estrutura de conteúdo, eles permitem criar componentes que são tanto altamente customizáveis quanto fáceis de usar. Seja construindo um design system ou organizando melhor seu código, dominar slots é essencial para o desenvolvimento moderno de web components.
Pronto para criar web components mais flexíveis? Comece refatorando um dos seus componentes existentes para usar slots. Foque em áreas onde você está atualmente passando HTML através de atributos ou usando estruturas de props complexas. Seu eu futuro (e sua equipe) agradecerão pelo código mais limpo e maintível.
FAQs
Props (atributos) são melhores para valores simples como strings, números ou booleanos. Slots se destacam ao aceitar conteúdo HTML complexo, múltiplos elementos ou qualquer estrutura de marcação. Use props para configuração e slots para conteúdo.
Sim, você pode modificar conteúdo slotted a qualquer momento, já que ele permanece no light DOM. Simplesmente selecione os elementos com atributos slot e atualize-os como qualquer outro elemento DOM. As mudanças se refletem imediatamente no componente renderizado.
Conteúdo slotted permanece no light DOM, tornando-o totalmente acessível para motores de busca e leitores de tela. Esta é uma grande vantagem sobre conteúdo shadow DOM, que pode ser mais difícil para crawlers indexarem.
Todos os elementos com o mesmo nome de slot aparecem naquele slot na ordem do documento. Isso é útil para criar layouts flexíveis onde usuários podem adicionar múltiplos itens a uma única área de slot.
Não, slots requerem shadow DOM para funcionar. Eles são especificamente projetados para projetar conteúdo light DOM em templates shadow DOM. Sem shadow DOM, você precisaria usar padrões diferentes de distribuição de conteúdo.