The promise of Web Components was that we’d get this convenience, but for a much wider range of HTML elements, developed much faster, as nobody needs to wait for the full spec + implementation process. We’d just include a script, and boom, we have more elements at our disposal!

Or, that was the idea. Somewhere along the way, the space got flooded by JS frameworks aficionados, who revel in complex APIs, overengineered build processes and dependency graphs that look like the roots of a banyan tree.

I think the issue is deeper than Verou’s complaints, although those are valid. Web Components don’t actually to solve the problem they purport to solve. The pitch is “get semantic elements from across the web!” But those are wrong problems to try to solve.


First of all, if search engines and screen readers don’t know about <my-tag>, it’s not semantic in any meaningful sense. There is already a very good way to provide semantic information to search engines: use JSON-LD to provide schema.org metadata. For accessibility, you need to either use the correct HTML5 element or use ARIA attributes, and this process is going to be the same whether you’re using Web Components or something else.

So next, “from across the web” is a performance killer. No matter how good networks and browsers get in the foreseeable future, you’re not going to be able to build a content page from multiple domains in under a second just due to the latencies involved in connecting, and that’s the performance goal content sites should be meeting. You need to at minimum host the scripts on your site. Once you’re doing that, the temptation to just go ahead and bundle them is pretty strong. Yes, HTTP2 makes the price of having several small modules much lower, but it’s hard to argue against minifying, concatenating, and tree shaking to pick up any remaining performance benefits. Now you’ve landed into the mess that it is modern JS bundling, for better or worse. Remember that best case here is that you end up at the fourth phase of the dialectic of dependencies: being overrun by low quality user written components.


Okay, so what about the JavaScript for Web Components themselves. They have this system in which you subclass HTMLElement and register the class as custom element with customElements.define(). What does this buy you over and above just using document.querySelectorAll("[data-my-element]")? Not a lot. It gets you a system of lifecycle hooks. Cool, I guess, but it doesn’t actually have any tools to manage your data lifecycle, so it’s not actually very useful. If you store your data in the DOM, it just becomes a bunch of strings on elements, and this stinks because most of you data is boolean (Is the widget open or closed? Am I waiting on a fetch to finish?) and has to be mutilated to fit into string (el.attr === "true" makes Alan Turing’s ghost cry). And forget about compound types like arrays and maps. The only practical solution there is to serialize the data to JSON and store that on the element, which is crazy. So, you can use the Web Component custom element lifecycle to manage element creation and deletion, but you need to have some other system for rationalizing the data lifecycle if your component is more complex than a <details> box.

Web Components are also associated with <template> and <slot> elements, but this is a substitute templating language, and it stinks. There are three competing schools of templating, each with its own niche. There are old school Mustache/Handlebars/Liquid/Jinja-style templates where the HTML is treated as raw text but with auto-escaping and some looping constructs. There are Angular/Vue/Alpine-style templates which treat HTML as a first class concept, and there are attributes for interpolation and looping. And there is JSX, which sort of combines the two, allowing for intermingling of regular JavaScript and pseudo-XML. <template>/<slot> is pretty much unusable because it doesn’t actually address the hard parts of templating, which are interpolating and looping. Again, it’s not buying you much versus just writing a quickie document.createElement helper function.

So what’s left is Shadow DOM. Shadow DOM is a feature for when you need to include an element on a page that breaks out of the styles of its parents. That is sometimes useful. But it is also extremely niche and should be part of CSS and not part of JS. The fundamental thing that Shadow DOM does is to allow an element of the page to have its own CSS reset. There’s no reason we couldn’t have that as part of CSS itself instead (perhaps by changing the rules for @import to allow it to be nested instead of only at the top level).


I am a person who is in the market for the sort of thing that Web Components are supposedly good at. For my day job, I distribute embed codes so that our partners can embed little widgets that let you sign up for the Spotlight PA newsletter from their sites. It’s supposed to be the clear use case. But even if I were willing to sacrifice IE11 compatibility (not a big deal but I prefer not to do so gratuitously), I don’t see why I should prefer that my embed code look like this:

<script src="https://example.com/embed.js" async></script>
<spl-embed version="1" src="https://www.spotlightpa.org/embeds/newsletter/"/>

Instead of what it actually looks like, which is this:

<script src="https://www.spotlightpa.org/embed.js" async></script>
<div
  data-spl-embed-version="1"
  data-spl-src="https://www.spotlightpa.org/embeds/newsletter/"
></div>

Yes, shorter tags would be nicer, but how does it actually benefit performance, search engines, or accessibility?

As it turns out, I do use Shadow DOM, but just to reset the CSS around my iframe to make sure it is responsively sized.

If I could get the ear of W3C, I would ask them to create a way for iframes to be properly sized without requiring JavaScript to communicate the size from within the iframe to the host page. That’s something that would actually make my life easier, unlike Web Components, which are basically just failed solutions to the wrong problems.