Skip to content

Default Slot

A <slot> element with no name attribute is the default slot. All light-DOM children that do not have a slot attribute are distributed there. You place the <slot> inside your shadow root, and the browser takes care of rendering the projected children at that position.

<my-box>
<p>This paragraph is projected into the default slot.</p>
</my-box>
class MyBox extends HTMLElement {
connectedCallback() {
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
:host { display: block; border: 2px solid #6366f1; padding: 1rem; border-radius: 8px; }
</style>
<slot><em>No content provided</em></slot>
`;
}
}
customElements.define('my-box', MyBox);

Content placed inside the <slot> element itself is the fallback. It renders only when no light-DOM children are projected into that slot. If the consumer provides children, the fallback is hidden; if they provide none, the fallback shows.

This is a zero-cost way to make a component self-documenting: the fallback text describes what the consumer is expected to supply.

The light-DOM child stays in the light DOM — only its rendering position moves to the slot. The node is not cloned or transferred. For example, document.querySelector('my-box').firstChild still returns the <p> element even after it has been projected into the shadow root. Event listeners attached to the light-DOM node continue to work normally.

OptionBenefitCost
A single default slotSimple, easy to use for a single-content use case, no extra syntax to learnCan’t separate content into multiple areas — switch to named slots if you need multiple layout spots
Fallback content inside <slot>Provides built-in documentation and a non-empty UI when no children are projectedAdds a little extra markup to every <slot>, and you have to keep it matching the real design
  • Thinking content projected into a slot is moved out of the light DOM — the node actually stays in the same light DOM; only its render position changes. Querying with document.querySelector still returns the same node.
  • Forgetting fallback content only shows when no children are projected at all — if even one child is projected, the fallback is hidden entirely, not shown alongside it.
  • Trying to style all slotted content from the shadow root with a deep ::slotted() — ::slotted() can only select the top-level projected element, not its descendants.

💡 Real-world examples

Lit uses the default slot as the basic pattern for content projection in components with free-form child content, like a wrapper or container component.

Shoelace combines a default slot with named slots in a component like <sl-card>, so the main content falls into the default slot while the header/footer use separate named slots.

Which light-DOM children are projected into the default slot?
When does fallback content inside a `<slot>` render?
After slot projection, where does the light-DOM child node live in the DOM?