Because most design systems are built on top of a framework rather than on the browser. When the framework changes, the components go with it. Web components sit one layer lower. They are part of the web platform itself, so a button you build once keeps working no matter what you rebuild around it.
We run into this on almost every rebuild of an older product site. The company has a component library, it is tied to a version of a framework nobody wants to upgrade, and rewriting the components is half the project. The design was fine. The foundation was rented.
Web components are one answer to that. They are not the right answer for every project, and we will be direct about where they fall down. But the underlying idea, which is that a reusable UI element should be a browser feature rather than a framework feature, has aged extremely well.
Web components are a set of browser standards for building reusable custom HTML elements with their internals kept private. MDN describes them as a suite of technologies that let you create reusable custom elements with their functionality encapsulated away from the rest of your code, then use them in your apps.
MDN groups the standard into three parts. Custom Elements is the JavaScript API for defining your own element and its behaviour. Shadow DOM is the API for attaching a private, separately rendered DOM tree to an element. HTML templates are the template and slot elements, which let you write markup that is not rendered until a component uses it.
The result is that you can write something like a card element or a rating element and use it in plain HTML. No build step is strictly required. No framework has to be present. The browser treats it as an element, because it is one.
Shadow DOM is the part that gives you real encapsulation. MDN describes it as attaching an encapsulated shadow tree to an element, rendered separately from the main document, so the element internals stay private and can be styled and scripted without colliding with the rest of the page.
That collision problem is the reason most large sites end up with unmanageable CSS. Two teams write a class called card, one of them wins, and nobody finds out until a page breaks in production. Shadow DOM removes the possibility rather than managing it with naming conventions.
It also cuts the other way, and this catches teams out. Styles from your global stylesheet do not reach inside a shadow tree by default. That is the feature working correctly, but it means theming has to be planned deliberately using custom properties and slots rather than assumed. We have seen teams adopt shadow DOM, discover their brand styles stop applying, and conclude the technology is broken. It is not. It is doing exactly what it says.
Effectively yes, for any browser people actually use. Can I Use lists global support for Custom Elements v1 at 96.04%, with full support across Chrome, Edge, Firefox, Opera, Samsung Internet, and the Android browsers, and partial support noted for Safari and Safari on iOS. Internet Explorer and Opera Mini are listed as not supporting it at all.
That number is the answer to the objection people still raise from memory. Web components had a genuinely rough first few years, and a lot of developers formed an opinion around 2018 and never revisited it. The support situation today is not the support situation then.
If you want a rigorous way to check this kind of thing rather than relying on a number you half remember, the Baseline project exists precisely for that. We wrote about how to use it in our guide to knowing when a web feature is safe to use. Checking takes two minutes and settles most arguments.
No, but most teams end up wanting one. You can define a custom element with plain JavaScript and no dependencies at all. The raw API is verbose for anything with state, though, which is why libraries exist to smooth it over without changing what you produce.
Lit is the most common choice. Its own site states that it weighs in at around 5 KB minified and compressed, and that it builds on the web components standards by adding reactivity, declarative templates, and a small set of features on top. Lit also states that it is now part of the OpenJS Foundation, which is a reasonable governance signal if you are betting a design system on it.
The important property is what Lit says about output. Every Lit component is a native web component, so the browser treats it like any other custom element. That means adopting the library does not re-create the lock-in you were trying to escape. You can drop Lit later and your elements still exist.
Five kilobytes is also small enough that it rarely shows up in a performance budget. That matters more than it sounds, because component libraries are one of the usual suspects when a bundle balloons. We went through the common causes in our piece on how to reduce JavaScript bundle size.
They can, if you render everything with client-side JavaScript and put your real content inside a shadow tree. Crawlers that do not execute JavaScript will see an empty custom element and nothing else. This is the single biggest risk with web components on a marketing site, and it is avoidable.
The fix is Declarative Shadow DOM, which lets a shadow root be expressed directly in HTML using a template element with a shadowrootmode attribute, rather than being built by JavaScript afterwards. Google web.dev explains that this exists because there was previously no built-in way to express shadow roots in server-generated HTML, and that it enables server-side rendering without the layout shifting and unstyled flash that came from attaching shadow roots after the fact.
Support is broadly there. According to web.dev, Declarative Shadow DOM is supported from Chrome 111, Edge 111, Firefox 123, and Safari 16.4. The same page notes the feature changed in 2023, including renaming shadowroot to shadowrootmode, and that the most up to date standardized versions landed in Chrome 124. If you are following an older tutorial, check which attribute name it uses.
Our rule on client work is blunt. Content that needs to rank goes in the static HTML, full stop. Web components are fine as long as they wrap content rather than generate it. We explained why this matters more every year in our article on why AI crawlers miss JavaScript-rendered content.
Usually not instead. Alongside. Frameworks like React, Vue, and Svelte solve application problems such as routing, data flow, and state across many screens. Web components solve the narrower problem of a reusable element. Treating them as competitors leads people to pick the wrong tool for whichever half of the job they were thinking about.
The pattern we like is a design system built as web components, consumed by whatever framework the product happens to use. The button, the field, the modal, and the badge are platform elements. The application logic stays in the framework. When the framework is replaced in four years, the visual layer survives.
Where that pattern gets awkward is form integration and complex data binding. Passing rich objects into an element and getting events back out is more ceremony than passing props in a framework. It is workable, and libraries help, but anyone who tells you it is seamless has not shipped a large form with it.
Three situations, in our experience. When one design system has to serve several products built with different stacks. When you need to embed a widget into a site you do not control. And when the thing you are building has to outlive the framework decision made around it.
The multi-stack case is the strongest. If marketing runs one platform, the app runs another, and the docs run a third, a shared web component library is the only version of a design system where the button is genuinely the same button. Everything else is three implementations agreeing to look similar.
The embed case is close behind. A custom element you can drop into any page with a script tag and a line of markup is far easier for a customer to install than a framework-specific package. The encapsulation also protects you from whatever CSS is already on their page, which is usually the real problem.
Skip them on a single marketing site built in one stack that you have no plans to change. The portability you are paying for is portability you will never use, and you will spend the time on ceremony instead of on the site. Simplicity is a real feature, and we choose it a lot more often than we choose flexibility.
We would also avoid them where the team has no appetite for the encapsulation model. Shadow DOM changes how styling and events work, and a team that has not internalised that will fight the browser for months. The technology is not hard. Working against its grain is.
And avoid them anywhere the content matters more than the component, unless you are committed to server rendering. Semantic, crawlable markup beats clever abstraction every time on a site whose job is to be found. That principle has not moved in twenty years, and we wrote about why in our piece on why semantic HTML still matters.
If you maintain a design system across more than one stack, yes, start now with a small set of elements rather than a full migration. If you run a single site on a single platform, no, and you are not missing out. The standard is mature and widely supported, which means there is no rush and no penalty for waiting until you have the actual problem it solves.
Our honest position is that web components are infrastructure, not a trend. They pay off on a five year horizon and look like overhead on a one year horizon. Be clear about which horizon you are on before you commit, because the answer changes completely.
If you are weighing this up for a rebuild and want a straight opinion rather than a sales pitch, we are happy to talk it through. Reach out at phoenix.studio and tell us what you are building. We will tell you if the simpler option is the better one, because quite often it is.
Tell us where you want to go. We'll tell you how we'd get you there.