What Are Web Components?
The problem: reuse without a framework
Section titled “The problem: reuse without a framework”Before Web Components, sharing a UI widget between projects meant either copying and pasting markup, or committing to a specific framework. A React button component cannot be dropped into a Vue app. A Svelte card cannot be reused in an Angular project.
Web Components solve this with three browser-native standards:
- Custom Elements — define new HTML tags as ES classes
- Shadow DOM — encapsulate the element’s DOM and CSS so styles cannot leak in or out
- HTML Templates and Slots — declare reusable markup fragments and insertion points declaratively
The result is a component that is:
| Property | Meaning |
|---|---|
| Reusable | Works in any HTML context, with any framework or none |
| Encapsulated | Internal markup and styles are isolated from the page |
| Native | No transpilation, no virtual DOM, no runtime library required |
Framework components vs Web Components
Section titled “Framework components vs Web Components”Framework components (React, Vue, Svelte) live entirely inside their framework’s runtime. The browser never sees a <MyButton> — it sees whatever the framework renders in its place.
<!-- React JSX — compiled away, never in the DOM --><MyButton variant="primary">Save</MyButton>
<!-- Web Component — a real DOM node the browser owns --><my-button variant="primary">Save</my-button>Web Components are actual DOM nodes. You can query them with document.querySelector, listen to their events with addEventListener, and inspect them in DevTools — just like any built-in element.
Browser support today
Section titled “Browser support today”All four major rendering engines support the complete Web Components v1 specification:
- Chrome / Edge — full support since 2018
- Firefox — full support since 2018
- Safari — full support since Safari 10.1 (2017) for Custom Elements; Shadow DOM v1 since Safari 10 (2016)
The one exception is customized built-ins (extending existing elements like HTMLButtonElement). Safari does not support them and has stated it will not — autonomous custom elements (extending HTMLElement) are the portable choice. This is covered in the final lesson of this module.
A custom element is just a class
Section titled “A custom element is just a class”At its core, a custom element is an ES class that extends HTMLElement and is registered with a tag name:
class MyGreeting extends HTMLElement { connectedCallback() { this.textContent = 'Hello from a custom element!'; }}
customElements.define('my-greeting', MyGreeting);After define, any <my-greeting> tag in the document — present or future — is automatically upgraded to an instance of MyGreeting.
Trade-offs
Section titled “Trade-offs”| Option | Benefit | Cost |
|---|---|---|
| Web Components (native standard) | Not tied to any framework, works anywhere a browser exists, no transpilation or runtime library needed | DX doesn’t match frameworks like React/Vue — no built-in reactive state, JSX, or ecosystem tooling |
| Customized built-ins (part of the spec) | Inherits a native element’s semantics for free | Safari refuses to support it, so it isn’t fully usable cross-browser in practice |
Common mistakes
Section titled “Common mistakes”- Thinking Web Components can fully replace a framework — in reality they are a browser-level building block; they have no routing, state management, or app-level tooling built in.
- Forgetting the hyphen rule when naming a tag — a tag name without a hyphen is not accepted as a valid custom element under the spec.
- Assuming all three standards (Custom Elements, Shadow DOM, Templates/Slots) are equally supported everywhere — customized built-ins are the notable exception Safari does not support, even though everything else is fully supported.
💡 Real-world examples
Adobe Spectrum — uses Web Components (Spectrum Web Components) as a design system shared across Adobe’s products and frameworks, without being tied to React or Vue. (opensource.adobe.com)
GitHub — ships autonomous custom elements like
<relative-time>and<clipboard-copy>as real open-source components, showing the platform-native model scales to production outside any single framework. (github/relative-time-element)