The first Web Components project we tried to ship — a shared widget set for two client microsites in 2017 — was a disaster. Shadow DOM didn't render server-side. Styling fell off a cliff at the encapsulation boundary. We needed a polyfill to support the browsers a client's CTO was still personally using. The widgets worked. Maintaining them did not.

We didn't touch the spec again until 2023, and we wrote off Web Components as a perpetually almost-ready technology that the industry would politely outgrow.

We were wrong about that. The thing that quietly fixed Web Components was not one big release. It was three or four pieces of plumbing landing across browsers in roughly the same window, plus a tooling ecosystem that finally stopped fighting them.

What actually changed

Declarative shadow DOM was the biggest one. Before it, a custom element rendered on the server was just an empty tag waiting for JavaScript to fill it in. That broke SSR in obvious ways and SEO in subtle ones. With <template shadowrootmode="open">, the server can ship the shadow tree in the HTML, the browser hydrates it as expected, and search engines see content rather than empty shells. Chrome shipped it in 2023, Safari followed in 2024, and as of last year it's something we can rely on across the browsers our clients care about.

Scoped CSS — the @scope rule — gave us the encapsulation benefit of shadow DOM without forcing the full shadow boundary in places where it caused more trouble than it solved. We use both now. Shadow DOM where we genuinely want isolation, @scope where we want "this CSS applies inside this subtree and nowhere else" without the additional ceremony.

Form-associated custom elements landed properly. A custom <my-input> can now participate in form submission, validation, and reset like a built-in input does. That sounds boring. It removes the single largest reason we used to refuse to build form controls as Web Components.

The libraries that sand the rough edges

Vanilla Web Components are writable but the ergonomics drag. Lit (from the Google team that used to own Polymer) is what we reach for now. It's small — under 6KB — reactive in the ways React conditioned us to want, and the templating uses tagged template literals that work without a build step if you don't want one. We've shipped Lit components into Vue apps, into Rails Hotwire apps, into static sites, and the integration story is the same in all of them: drop in a script tag, use the custom element, move on.

Stencil is the other one worth knowing. It's heavier than Lit, and it compiles to vanilla Web Components, but the compile step gives you TypeScript-first DX and lets you target older browsers without writing the fallbacks yourself. We use Stencil when a client's design-system team is going to maintain the components long-term — the DX matters more than the bundle size at that scale.

Where they actually beat framework components

Three concrete cases from this year.

Long-lived design systems. A client's design system needs to survive three frontend rewrites. The 2019 site was Angular. The 2022 rebuild was React. They're scoping a 2026 migration to Svelte for the marketing surface and keeping React for the admin. A design system built as Web Components rides through all of that. A design system built as React components becomes a rewrite line item every time the host framework changes.

Multi-framework environments. Bigger clients have multiple frontend teams using different stacks. Common pieces — auth modals, navigation chrome, footers — used to be reimplemented per team or papered over with iframes. We've shipped two of these as Web Components this year. Each team imports the same component. Each team styles it through CSS custom properties exposed at the boundary. Nobody argues about React versions.

Embedded widgets. A SaaS client ships a "powered by them" widget that customers embed on their own sites. A React widget would force every customer to either bundle React or accept a duplicate copy of it on the page. A Web Component is just a script tag and a custom element. The customer's stack is irrelevant. This is the use case where Web Components have always made the most sense, but only with declarative shadow DOM does the embed look right before the script loads.

Where we still don't reach for them

Application UI with deep client state and a single framework already in play. If your team is shipping a React app and a feature is a React feature, write it in React. Web Components add ceremony for no benefit when the host environment is uniform. We see this mistake on roughly half the "should we use Web Components" calls we take. The honest answer is often no, because the assumption that frameworks would be a problem hasn't actually become a problem yet.

The spec we tried to ship in 2017 was not ready. The one we'd ship today is. That's not a small thing for a piece of platform technology — most of them never make that crossing.