QUICK START:HTMLCSSJavaScriptTypeScriptPythonSQLReactNext.jsNode.jsLinux & UbuntuKotlinSwiftC# / .NETJavaGoRustC++DSASystem DesignDevOpsCybersecurityAI / ML
Intermediate 22 min readModule: Module 13: Web Components: Custom Elements, Shadow DOM & Templates

Custom Elements v1, Shadow DOM & Template Cloning

Create native, reusable, and framework-independent UI widgets using HTML5 Custom Elements, encapsulated Shadow DOM styles, and declarative templates.

What You Will Learn in This Lesson

  • The 3 pillars of Web Components: Custom Elements, Shadow DOM, and HTML Templates
  • Encapsulating CSS styles to prevent global stylesheet leakage using `attachShadow({ mode: 'open' })`
  • Managing custom element lifecycle callbacks: `connectedCallback`, `disconnectedCallback`, and `attributeChangedCallback`
  • Declarative Shadow DOM (`<template shadowrootmode='open'>`) for Server-Side Rendering (SSR)

Introduction & Core Concept

Web Components are a suite of browser-native technologies that allow developers to create custom, reusable, and encapsulated HTML tags (like <kwas-video-player>) that work natively across React, Angular, Vue, or vanilla HTML without framework dependencies.
WHY DOES THIS MATTER IN THE REAL WORLD?

Enterprise design systems built on Web Components survive framework obsolescence. Components written today will continue to work unchanged for decades across all web browsers.

Syntax & Structure

html
class CustomCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
}
customElements.define('kwas-card', CustomCard);

A Complete Encapsulated Web Component with Shadow DOM

html
html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Custom Elements & Shadow DOM Architecture</title>
</head>
<body>
<!-- Declarative Custom Element Instance -->
<kwas-badge variant="pro" label="Enterprise Architecture"></kwas-badge>
<!-- Component Template -->
<template id="kwas-badge-template">
<style>
:host {
display: inline-block;
font-family: system-ui, sans-serif;
}
.badge-container {
padding: 4px 12px;
border-radius: 9999px;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
background: linear-gradient(135deg, #3b82f6, #8b5cf6);
color: #ffffff;
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
}
</style>
<span class="badge-container" part="badge">
<slot name="prefix"></slot>
<span class="badge-text"></span>
</span>
</template>
<script>
class KwasBadge extends HTMLElement {
static get observedAttributes() { return ['label']; }
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
const template = document.getElementById('kwas-badge-template');
shadow.appendChild(template.content.cloneNode(true));
}
connectedCallback() {
this.updateLabel();
}
attributeChangedCallback(name, oldVal, newVal) {
if (name === 'label' && oldVal !== newVal) {
this.updateLabel();
}
}
updateLabel() {
const textElem = this.shadowRoot.querySelector('.badge-text');
if (textElem) {
textElem.textContent = this.getAttribute('label') || 'Default Badge';
}
}
}
customElements.define('kwas-badge', KwasBadge);
</script>
</body>
</html>

Line-by-Line Technical Breakdown

1Declarative Shadow DOM: Modern browsers support `<template shadowrootmode='open'>`. This allows server-side rendered HTML (from Next.js or Astro) to stream pre-rendered Shadow DOM trees to the client before JavaScript hydrates.

Try It Yourself (Interactive Editor)

Modify the code in real-time and click Run to test live browser output and console logs.

Intelligent Code Runner & Live Sandbox[HTML]
HTML SOURCE EDITOR
Interactive Live Code

Common Mistakes & How to Avoid Them

#1: Naming a custom element without a hyphen (e.g., customElements.define('badge', Badge)).

The W3C specification strictly requires custom elements to include at least one hyphen to avoid collisions with future native HTML elements.

Incorrect / Antipattern
customElements.define('mybadge', MyBadge);
Correct / Professional Solution
customElements.define('my-badge', MyBadge);

Industry Best Practices & Professional Standards

  • Always include a hyphen in custom element names (e.g. `app-card`, `kwas-player`).
  • Use `<slot>` elements to provide flexible content projection points.
  • Use the `::part()` pseudo-element to expose styled micro-components safely to parent stylesheets.

Lesson Summary & Core Takeaways

  • Web Components deliver framework-independent, reusable native HTML widgets.
  • Shadow DOM guarantees 100% style and DOM encapsulation.
  • Lifecycle callbacks manage component mounting, unmounting, and attribute changes cleanly.