The platform's native component system: define <user-card> once, use it like any tag โ no framework, works everywhere forever.
A complete custom element
class UserCard extends HTMLElement {
connectedCallback() {
const name = this.getAttribute("name") ?? "Unknown";
this.attachShadow({ mode: "open" });
this.shadowRoot.innerHTML = `
<style>
/* scoped! outside CSS can't touch this */
.card { border: 1px solid #444; border-radius: 10px; padding: 12px; }
</style>
<div class="card">
<strong>${name}</strong>
<slot></slot> <!-- children render here -->
</div>`;
}
}
customElements.define("user-card", UserCard);<user-card name="Priya">CSE ยท CGPA 8.9</user-card>
The three pillars
- Custom elements: your own tags with lifecycle callbacks
- Shadow DOM: style/markup encapsulation โ global CSS can't leak in or out
- Slots: children projection (like React children)
Honest positioning
Frameworks still win for full apps (state, routing, ecosystem). Web components shine for share-anywhere widgets โ one component used across a React app, a WordPress page and a plain HTML site. Also great interview trivia: "how would you scope styles without a framework?" โ shadow DOM.