Global Attribute
The slot attribute is used in Web Components to assign content to specific slots within a Shadow DOM template. It enables flexible content projection, allowing component users to customize parts of a component while maintaining encapsulation.
< p slot = " header " > This goes in the header slot </ p >
< p > This goes in the default slot </ p >
Value Description Slot name (string) Name of the slot to project content into (empty/omitted) Content goes into the default (unnamed) slot
Interactive code playground requires JavaScript. Here's the code:
<!DOCTYPE html>
<html>
<head>
<style>
user-card {
display: block;
margin: 20px;
}
</style>
</head>
<body>
<h2>User Profile Card</h2>
<user-card>
<img slot="avatar" src="https://ui-avatars.com/api/?name=John+Doe&size=80" alt="Avatar">
<span slot="name">John Doe</span>
<span slot="role">Software Engineer</span>
<p slot="bio">Passionate about web development and open source. Love building great user experiences.</p>
</user-card>
<script>
class UserCard extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
:host {
display: block;
background: white;
border: 2px solid #e2e8f0;
border-radius: 12px;
padding: 24px;
max-width: 400px;
}
.card-header {
display: flex;
align-items: center;
gap: 15px;
margin-bottom: 20px;
}
::slotted([slot="avatar"]) {
width: 80px;
height: 80px;
border-radius: 50%;
border: 3px solid #3b82f6;
}
.info h3 {
margin: 0;
color: #1e293b;
font-size: 20px;
}
::slotted([slot="name"]) {
font-weight: bold;
font-size: 20px;
color: #1e293b;
}
::slotted([slot="role"]) {
color: #64748b;
font-size: 14px;
}
::slotted([slot="bio"]) {
color: #64748b;
line-height: 1.6;
margin: 15px 0 0;
}
</style>
<div class="card-header">
<slot name="avatar"></slot>
<div class="info">
<div><slot name="name"></slot></div>
<div><slot name="role"></slot></div>
</div>
</div>
<slot name="bio"></slot>
`;
}
}
customElements.define('user-card', UserCard);
</script>
</body>
</html>
Interactive code playground requires JavaScript. Here's the code:
<!DOCTYPE html>
<html>
<head>
<style>
alert-box {
display: block;
margin: 15px 0;
}
</style>
</head>
<body>
<h2>Alert Boxes with Slots</h2>
<!-- Success alert with custom title -->
<alert-box type="success">
<strong slot="title">Operation Successful!</strong>
Your changes have been saved successfully.
</alert-box>
<!-- Warning alert with default title -->
<alert-box type="warning">
Your session will expire in 5 minutes.
</alert-box>
<!-- Error alert with custom title and action -->
<alert-box type="error">
<strong slot="title">Error Occurred</strong>
Failed to load data from server.
<button slot="action" onclick="location.reload()">Retry</button>
</alert-box>
<script>
class AlertBox extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
const type = this.getAttribute('type') || 'info';
const colors = {
success: { bg: '#dcfce7', border: '#10b981', text: '#166534' },
warning: { bg: '#fef3c7', border: '#f59e0b', text: '#92400e' },
error: { bg: '#fee', border: '#ef4444', text: '#b91c1c' },
info: { bg: '#dbeafe', border: '#3b82f6', text: '#1e40af' }
};
const icons = {
success: '✓',
warning: '⚠',
error: '✗',
info: 'ℹ'
};
const color = colors[type];
const icon = icons[type];
shadow.innerHTML = `
<style>
:host {
display: block;
background: ${color.bg};
border: 2px solid ${color.border};
border-radius: 8px;
padding: 16px;
color: ${color.text};
}
.alert-header {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 8px;
}
.icon {
font-size: 24px;
font-weight: bold;
}
::slotted([slot="title"]) {
margin: 0;
font-size: 16px;
}
.alert-body {
margin-left: 34px;
}
::slotted([slot="action"]) {
margin-top: 10px;
padding: 6px 16px;
background: ${color.border};
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 600;
}
</style>
<div class="alert-header">
<span class="icon">${icon}</span>
<slot name="title">${type.charAt(0).toUpperCase() + type.slice(1)}</slot>
</div>
<div class="alert-body">
<slot></slot>
<div><slot name="action"></slot></div>
</div>
`;
}
}
customElements.define('alert-box', AlertBox);
</script>
</body>
</html>
Interactive code playground requires JavaScript. Here's the code:
<!DOCTYPE html>
<html>
<head>
<style>
tab-group {
display: block;
margin: 20px;
}
</style>
</head>
<body>
<h2>Tabbed Interface</h2>
<tab-group>
<div slot="tab">Profile</div>
<div slot="tab">Settings</div>
<div slot="tab">Notifications</div>
<div slot="panel">
<h3>Profile</h3>
<p>View and edit your profile information.</p>
</div>
<div slot="panel">
<h3>Settings</h3>
<p>Configure your account settings and preferences.</p>
</div>
<div slot="panel">
<h3>Notifications</h3>
<p>Manage your notification preferences.</p>
</div>
</tab-group>
<script>
class TabGroup extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
:host {
display: block;
border: 2px solid #e2e8f0;
border-radius: 12px;
overflow: hidden;
background: white;
}
.tabs {
display: flex;
background: #f8fafc;
border-bottom: 2px solid #e2e8f0;
}
::slotted([slot="tab"]) {
flex: 1;
padding: 15px;
text-align: center;
cursor: pointer;
border: none;
background: transparent;
transition: all 0.2s;
}
::slotted([slot="tab"]:hover) {
background: #f1f5f9;
}
::slotted([slot="tab"].active) {
background: white;
color: #3b82f6;
border-bottom: 3px solid #3b82f6;
}
.panels {
padding: 20px;
}
::slotted([slot="panel"]) {
display: none;
}
::slotted([slot="panel"].active) {
display: block;
}
</style>
<div class="tabs">
<slot name="tab"></slot>
</div>
<div class="panels">
<slot name="panel"></slot>
</div>
`;
this.currentTab = 0;
}
connectedCallback() {
const tabs = this.querySelectorAll('[slot="tab"]');
const panels = this.querySelectorAll('[slot="panel"]');
tabs.forEach((tab, index) => {
tab.addEventListener('click', () => this.switchTab(index));
});
this.switchTab(0);
}
switchTab(index) {
const tabs = this.querySelectorAll('[slot="tab"]');
const panels = this.querySelectorAll('[slot="panel"]');
tabs.forEach((tab, i) => {
tab.classList.toggle('active', i === index);
});
panels.forEach((panel, i) => {
panel.classList.toggle('active', i === index);
});
this.currentTab = index;
}
}
customElements.define('tab-group', TabGroup);
</script>
</body>
</html>
Interactive code playground requires JavaScript. Here's the code:
<!DOCTYPE html>
<html>
<head>
<style>
fancy-card {
display: block;
margin: 20px 0;
}
</style>
</head>
<body>
<h2>Product Cards</h2>
<fancy-card>
<img slot="image" src="https://via.placeholder.com/400x200/3b82f6/ffffff?text=Product+Image" alt="Product">
<span slot="badge">New</span>
<h3 slot="title">Wireless Headphones</h3>
<p slot="description">Premium noise-cancelling headphones with 30-hour battery life.</p>
<span slot="price">$299.99</span>
<button slot="action">Add to Cart</button>
</fancy-card>
<fancy-card>
<img slot="image" src="https://via.placeholder.com/400x200/10b981/ffffff?text=Product+Image" alt="Product">
<span slot="badge">Sale</span>
<h3 slot="title">Smart Watch</h3>
<p slot="description">Track your fitness and stay connected with our latest smartwatch.</p>
<span slot="price">$199.99</span>
<button slot="action">Add to Cart</button>
</fancy-card>
<script>
class FancyCard extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
:host {
display: block;
border: 2px solid #e2e8f0;
border-radius: 12px;
overflow: hidden;
background: white;
max-width: 400px;
transition: transform 0.2s, box-shadow 0.2s;
}
:host(:hover) {
transform: translateY(-4px);
box-shadow: 0 10px 30px rgba(0,0,0,0.15);
}
.image-container {
position: relative;
overflow: hidden;
}
::slotted([slot="image"]) {
width: 100%;
height: 200px;
object-fit: cover;
display: block;
}
::slotted([slot="badge"]) {
position: absolute;
top: 10px;
right: 10px;
background: #ef4444;
color: white;
padding: 4px 12px;
border-radius: 4px;
font-size: 12px;
font-weight: bold;
}
.content {
padding: 20px;
}
::slotted([slot="title"]) {
margin: 0 0 10px;
color: #1e293b;
font-size: 20px;
}
::slotted([slot="description"]) {
color: #64748b;
line-height: 1.6;
margin: 0 0 15px;
}
.footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 20px;
border-top: 2px solid #f1f5f9;
}
::slotted([slot="price"]) {
font-size: 24px;
font-weight: bold;
color: #10b981;
}
::slotted([slot="action"]) {
padding: 10px 20px;
background: #3b82f6;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
}
</style>
<div class="image-container">
<slot name="image"></slot>
<slot name="badge"></slot>
</div>
<div class="content">
<slot name="title"></slot>
<slot name="description"></slot>
</div>
<div class="footer">
<slot name="price"></slot>
<slot name="action"></slot>
</div>
`;
}
}
customElements.define('fancy-card', FancyCard);
</script>
</body>
</html>
The slot attribute assigns light DOM content to specific slots in the shadow DOM:
<!-- Light DOM (what the user writes) -->
< p slot = " header " > Header content </ p >
<!-- Shadow DOM (inside the component) -->
< slot name = " header " ></ slot > <!-- Receives slot="header" content -->
< slot ></ slot > <!-- Receives content without slot attribute -->
Named slots : Require slot="name" attribute
< p slot = " header " > Goes to < slot name = " header " ></ slot ></ p >
Default slot : No slot attribute needed
< p > Goes to < slot ></ slot ></ p >
<!-- Good: Clear, semantic names -->
< h3 slot = " title " > Title </ h3 >
< p slot = " description " > Description </ p >
< button slot = " action " > Action </ button >
<!-- Bad: Generic names -->
< h3 slot = " slot2 " > Title </ h3 >
// In shadow DOM, provide defaults
<slot name="title">Default Title</slot>
<slot>Default content</slot>
* - header: Component header content
* - default: Main content
* - footer: Footer actions
class MyComponent extends HTMLElement {
::slotted([ slot = " title " ] ) {
class MyComponent extends HTMLElement {
this . attachShadow ({ mode: ' open ' });
// Get all slotted elements
const slot = this . shadowRoot . querySelector ( ' slot[name="header"] ' );
const elements = slot . assignedElements ();
console . log ( ' Slotted elements: ' , elements );
// Listen for slot changes
slot . addEventListener ( ' slotchange ' , ( e ) => {
console . log ( ' Slot content changed ' );
const element = document . createElement ( ' p ' );
element . textContent = ' Dynamic content ' ;
element . slot = ' header ' ; // Assign to named slot
myComponent . appendChild ( element );
< img slot = " image " src = " product.jpg " >
< h3 slot = " title " > Product Name </ h3 >
<!-- Description slot is optional -->
< span slot = " price " > $99.99 </ span >
< h2 slot = " header " > Confirm Action </ h2 >
< p slot = " body " > Are you sure you want to continue? </ p >
Browser Support Chrome 53+ Firefox 63+ Safari 10+ Edge 79+