Web Components & Custom Elements
Web Components let you create reusable, encapsulated HTML elements with their own markup, styles, and behavior. They consist of Custom Elements (define new tags), Shadow DOM (encapsulated styling), and HTML Templates (<template>/<slot>).
Web Components Overview
Web Components are a set of browser APIs that let you create custom, reusable HTML elements. Unlike React or Vue components, Web Components are native browser features — they work everywhere without a framework. The three pillars: Custom Elements (class extends HTMLElement, customElements.define('my-component')), Shadow DOM (encapsulated DOM tree with scoped CSS), and Templates (<template> holds reusable markup, <slot> allows content injection).
Web Components are native browser features — no framework needed
Custom Element Code
// Define a custom element
class UserCard extends HTMLElement {
constructor() {
super();
// Attach shadow DOM for style encapsulation
const shadow = this.attachShadow({ mode: 'open' });
const name = this.getAttribute('name') || 'Anonymous';
const role = this.getAttribute('role') || 'Member';
shadow.innerHTML = `
<style>
.card {
border: 2px solid #E44D26;
border-radius: 12px;
padding: 16px;
font-family: Arial, sans-serif;
max-width: 250px;
}
.name { font-weight: bold; font-size: 1.2em; }
.role { color: #666; margin-top: 4px; }
</style>
<div class="card">
<div class="name">${name}</div>
<div class="role">${role}</div>
<slot></slot>
</div>
`;
}
// Lifecycle callbacks
connectedCallback() { console.log('Card added to page'); }
disconnectedCallback() { console.log('Card removed'); }
attributeChangedCallback(name, oldVal, newVal) {
console.log(`${name}: ${oldVal} → ${newVal}`);
}
static get observedAttributes() { return ['name', 'role']; }
}
// Register the custom element
customElements.define('user-card', UserCard);
// Usage in HTML:
// <user-card name="Jane Doe" role="Developer">
// <p>Extra content goes in the slot</p>
// </user-card>Warning
Custom element names MUST contain a hyphen (dash). Names like <user-card>, <app-header>, <my-button> are valid. Names without hyphens like <usercard> or <mybutton> are INVALID and will be treated as unknown HTML elements. This rule ensures custom elements never conflict with current or future native HTML elements.