Declarative Approach
📅
Declarative Shadow DOM is a modern way to create isolated DOM trees directly in HTML markup, without using JavaScript. 🎯
🌟 What is Declarative Shadow DOM?
Declarative Shadow DOM allows you to define Shadow DOM directly in HTML tags using the special shadowrootmode attribute. This is a revolutionary approach that works even before JavaScript loads!
📋 Basic Syntax
<my-component>
<template shadowrootmode="open">
<style>
:host {
display: block;
padding: 20px;
border: 2px solid #4CAF50;
}
</style>
<h2>Declarative Component</h2>
<slot></slot>
</template>
<p>Content from Light DOM</p>
</my-component>
🎯 Operation Modes
🔓 Open Mode (open)
<template shadowrootmode="open">
<!-- Shadow DOM accessible via element.shadowRoot -->
<slot name="content"></slot>
</template>
🔒 Closed Mode (closed)
<template shadowrootmode="closed">
<!-- Shadow DOM not accessible from outside -->
<span>Closed content</span>
</template>
🚀 Practical Examples
Example 1: Profile Card
<user-profile>
<template shadowrootmode="open">
<style>
.profile-card {
border: 1px solid #ddd;
border-radius: 12px;
padding: 20px;
max-width: 300px;
}
.avatar {
width: 80px;
height: 80px;
border-radius: 50%;
background: #f0f0f0;
}
</style>
<div class="profile-card">
<div class="avatar"></div>
<h3><slot name="name">Username</slot></h3>
<p><slot name="bio">Profile description...</slot></p>
</div>
</template>
<span slot="name">Anna Petrova</span>
<span slot="bio">Frontend developer passionate about Web Components</span>
</user-profile>
Example 2: Icon Button
<icon-button>
<template shadowrootmode="open">
<style>
button {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 20px;
border: none;
border-radius: 6px;
background: #2196F3;
color: white;
cursor: pointer;
}
</style>
<button>
<slot name="icon">⭐</slot>
<slot>Button</slot>
</button>
</template>
<span slot="icon">🚀</span>
Get Started
</icon-button>
🔄 Integration with Imperative Code
Declarative Shadow DOM works perfectly with JavaScript:
class DeclarativeComponent extends HTMLElement {
constructor() {
super();
// Shadow DOM already created declaratively!
console.log('Shadow Root:', this.shadowRoot);
}
connectedCallback() {
// Add dynamic behavior
if (this.shadowRoot) {
this.shadowRoot.querySelector('button').addEventListener('click', () => {
this.dispatchEvent(new CustomEvent('button-clicked'));
});
}
}
}
customElements.define('declarative-component', DeclarativeComponent);
📊 Comparison with Imperative Approach
| Characteristic | 🔄 Declarative | ⚡ Imperative |
|---|---|---|
| Syntax | HTML markup | JavaScript code |
| Creation time | During HTML parsing | During JS execution |
| Performance | ✅ Higher | ⚡ High |
| SSR support | ✅ Full | ❌ Limited |
| Browser support | 🟡 Modern browsers | ✅ All browsers |
🎯 Advantages of Declarative Approach
✅ Instant Rendering
Components render before JavaScript loads
✅ Better SEO
Search engines see Shadow DOM content
✅ Server-Side Rendering
Full server-side rendering support
✅ Development Simplicity
Less JavaScript code, more semantic markup
⚠️ Limitations and Considerations
Browser Support
// Feature detection
if (HTMLTemplateElement.prototype.hasOwnProperty('shadowRootMode')) {
console.log('Declarative Shadow DOM supported');
} else {
console.log('Polyfill or imperative approach required');
}
Fallback Strategy
<my-component>
<template shadowrootmode="open" id="shadow-template">
<!-- Declarative Shadow DOM -->
</template>
<script>
// Fallback for old browsers
if (!element.shadowRoot) {
const template = document.getElementById('shadow-template');
element.attachShadow({ mode: 'open' }).appendChild(template.content.cloneNode(true));
}
</script>
</my-component>
🏗️ Advanced Techniques
Dynamic Style Updates
class ThemedComponent extends HTMLElement {
static observedAttributes = ['theme'];
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'theme' && this.shadowRoot) {
this.shadowRoot.host.setAttribute('theme', newValue);
}
}
}
Nested Declarative Shadow DOM
<outer-component>
<template shadowrootmode="open">
<style>:host { padding: 20px; }</style>
<h2>Outer Component</h2>
<inner-component>
<template shadowrootmode="open">
<style>:host { margin: 10px; }</style>
<p>Inner Component</p>
</template>
</inner-component>
</template>
</outer-component>
🔧 Tools and Polyfills
Polyfill for Old Browsers
// Install: npm install @webcomponents/template-shadowroot
import '@webcomponents/template-shadowroot';
// Or via CDN
<script src="https://unpkg.com/@webcomponents/template-shadowroot"></script>
Build Tool Support
Webpack, Rollup and Vite automatically handle declarative Shadow DOM. For optimization:
// vite.config.js
export default {
build: {
polyfillModulePreload: false // Faster loading
}
}
🎯 Best Practices
- Use for static content 📄 - ideal for UI components
- Combine with imperative code 🔄 - dynamic behavior via JS
- Test in different browsers 🌐 - provide fallback solutions
- Optimize styles 🎨 - use CSS variables for themes
- Document slots 📖 - help other developers
🚀 Example: Complete UI Component
<modal-dialog open>
<template shadowrootmode="open">
<style>
:host {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.5);
display: flex;
align-items: center;
justify-content: center;
}
.dialog {
background: white;
padding: 24px;
border-radius: 8px;
max-width: 500px;
}
</style>
<div class="dialog">
<header>
<h3><slot name="title">Title</slot></h3>
<button id="close">×</button>
</header>
<main>
<slot>Modal content...</slot>
</main>
</div>
</template>
<span slot="title">Greeting</span>
<p>Welcome to the world of declarative Shadow DOM!</p>
</modal-dialog>
📈 Performance and Optimization
Declarative approach provides:
- Faster FMP (First Meaningful Paint) ⚡
- Reduced Time to Interactive 🎯
- Better Core Web Vitals 📊
- Efficient Caching 💾
🎉 Conclusion
Declarative Shadow DOM is a powerful tool for creating fast, SEO-friendly web components. Combine it with imperative approach for maximum flexibility and performance! 🚀