Web Components have been "the future" for nearly a decade. The specs are finalized, browser support is universal, and yet most developers still reach for React or Vue. That says something — but it doesn't mean Web Components are useless. They shine in specific scenarios where framework-agnostic reusability matters.
At Pillai Infotech, we use Web Components for shared UI elements across projects that use different frameworks — a consistent date picker, a notification banner, a cookie consent widget that works whether the host page is React, Vue, WordPress, or static HTML.
What Are Web Components
Web Components are a set of browser-native APIs that let you create reusable, encapsulated HTML elements. They're not a framework — they're web standards built into every modern browser.
<!-- Using a Web Component is just like any HTML element -->
<user-card name="John Doe" role="Developer" avatar="/john.jpg"></user-card>
<!-- Works in React -->
function App() {
return <user-card name="John" role="Dev" />;
}
<!-- Works in Vue -->
<template>
<user-card :name="user.name" :role="user.role" />
</template>
<!-- Works in plain HTML — no framework needed -->
The Three Specifications
1. Custom Elements
Define new HTML tags with their own behavior. The browser treats them like native elements.
2. Shadow DOM
Encapsulates styles and markup inside a component. CSS from outside doesn't leak in, CSS from inside doesn't leak out. True isolation.
3. HTML Templates
<template> and <slot> elements for defining component structure and composition points.
Building a Web Component
class UserCard extends HTMLElement {
// Observed attributes trigger attributeChangedCallback
static observedAttributes = ['name', 'role', 'avatar'];
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.render();
}
attributeChangedCallback() {
this.render();
}
render() {
const name = this.getAttribute('name') || 'Unknown';
const role = this.getAttribute('role') || '';
const avatar = this.getAttribute('avatar') || '';
this.shadowRoot.innerHTML = `
<style>
:host {
display: flex;
gap: 1rem;
padding: 1rem;
border: 1px solid #e2e8f0;
border-radius: 8px;
font-family: system-ui;
}
img { width: 48px; height: 48px; border-radius: 50%; }
.name { font-weight: 600; }
.role { color: #64748b; font-size: 0.9rem; }
</style>
${avatar ? `<img src="${avatar}" alt="${name}">` : ''}
<div>
<div class="name">${name}</div>
<div class="role">${role}</div>
<slot></slot>
</div>
`;
}
}
// Register the element
customElements.define('user-card', UserCard);
The Shadow DOM means this component's styles won't conflict with your page's CSS — and vice versa. The <slot> element lets consumers inject content inside the component.
When Web Components Make Sense
- Design system shared across frameworks — one set of components for React, Vue, Angular, and static pages
- Embeddable widgets — chatbots, calendars, payment forms that must work on any website
- Micro-frontends — teams using different frameworks contributing to the same page
- CMS/WordPress plugins — interactive elements in content-managed pages
- Long-lived UI components — standards outlast frameworks
Honest Limitations
- Verbose API. Vanilla Web Components require more boilerplate than React/Vue components. Use Lit to reduce this.
- No built-in reactivity. You manage state updates manually. No virtual DOM, no reactive declarations.
- SSR is hard. Shadow DOM doesn't render on the server easily. Declarative Shadow DOM (new spec) helps but isn't universal yet.
- React integration quirks. React doesn't handle Web Component events and properties perfectly — you need wrapper libraries or refs.
- Form integration. Custom Elements don't participate in HTML forms by default.
ElementInternalsAPI adds this but it's extra work.
Libraries and Tools
| Library | Size | Best For |
|---|---|---|
| Lit | ~5 KB | Best DX, reactive properties, decorators. Google-backed. |
| Stencil | ~3 KB runtime | Compiler-based, generates framework wrappers. Ionic uses it. |
| Shoelace (now Web Awesome) | Tree-shakeable | Ready-made component library built on Lit. |
| Vanilla | 0 KB | Simple components, no dependencies needed. |
Our recommendation: Use Lit for building Web Components. It adds reactive properties, declarative templates, and a clean API while staying tiny. Vanilla Custom Elements are fine for very simple components.