Web Components: Native, Reusable UI Without a Framework
author: Madan |
date: |
read time: ~1 min
tags: [webcomponents] [js]
Web Components are the browser’s native way to create reusable UI elements with their own styles and logic, no framework required.
Web Components let you create custom HTML elements with encapsulated styles and behavior, using browser APIs. No framework required. They work in React, Angular, plain HTML, whatever. That portability is the whole point.
1. Define a Custom Element
class MyBadge extends HTMLElement {
connectedCallback() {
const label = this.getAttribute("label") ?? "Default";
this.innerHTML = `<span class="badge">${label}</span>`;
}
}
customElements.define("my-badge", MyBadge);Use it anywhere:
<my-badge label="New"></my-badge>2. Add Style Isolation with Shadow DOM
class MyCard extends HTMLElement {
connectedCallback() {
const root = this.attachShadow({ mode: "open" });
root.innerHTML = `
<style>
.card {
padding: 12px;
border-radius: 12px;
border: 1px solid #ddd;
font-family: sans-serif;
}
</style>
<div class="card">
<slot></slot>
</div>
`;
}
}
customElements.define("my-card", MyCard);
Usage:
<my-card>
Hello inside a styled box
</my-card>The .card styles don’t leak out. Outside CSS doesn’t break it. Finally, peace.
3. Reactivity via Attributes
class MyCounter extends HTMLElement {
static get observedAttributes() {
return ["count"];
}
attributeChangedCallback() {
this.render();
}
connectedCallback() {
if (!this.hasAttribute("count")) this.setAttribute("count", "0");
this.render();
this.addEventListener("click", () => {
this.count++;
});
}
get count() {
return Number(this.getAttribute("count"));
}
set count(v) {
this.setAttribute("count", String(v));
}
render() {
this.textContent = `Count: ${this.count}`;
}
}
customElements.define("my-counter", MyCounter);No framework state. Just attributes and lifecycle.
Why Use Web Components
- Framework-agnostic: write once, use in any stack.
- Encapsulation: Shadow DOM prevents CSS collisions.
- Longevity: browser standard, not tied to ecosystem churn.
- Great for: design systems, widgets, microfrontends, shared UI across teams.
They’re not a full app framework. They’re a native component primitive. Use them where portability and isolation matter more than DX sugar.