Why I don't use web components

richharris profile image Rich Harris Updated on ・7 min read

For my first post on dev.to I thought I'd write about a nice, safe topic that's free of controversy: web components.

I'm mostly writing this for my future self, so that I have something to point to next time someone asks why I'm a web component skeptic, and why Svelte doesn't compile to custom elements by default. (It can compile to CEs, and it can consume CEs as evidenced by its perfect score on Custom Elements Everywhere.)

None of this should be taken as criticism of the hard work that has been done on web components. It's possible that I have made some errors in this post, in which case I'd welcome corrections.

Nor am I saying that you shouldn't use web components. They do have valid use cases. I'm just explaining why I don't.

1. Progressive enhancement

This may be an increasingly old-fashioned view, but I think that websites should work without JavaScript wherever possible. Web components don't.

That's fine for things that are intrinsically interactive, like a custom form element (<cool-datepicker>), but it's not fine for your nav bar. Or consider a simple <twitter-share> element that encapsulates all the logic for constructing a Twitter web intent URL. I could build it in Svelte and it would generate server-rendered HTML like this:

<a target="_blank" noreferrer href="..." class="svelte-1jnfxx">
  Tweet this

In other words, a bog-standard <a> element, in all its accessible glory.

With JavaScript enabled, it progressively enhances — rather than opening a new tab, it opens a small popup window instead. But without, it still works fine.

By contrast, the web component HTML would look something like this...

<twitter-share text="..." url="..." via="..."/>

...which is useless and inaccessible, if JS is disabled or somehow broken, or the user is on an older browser.

The class="svelte-1jnfxx" is what enables encapsulated styles without Shadow DOM. Which brings me onto my next point:

2. CSS in, err... JS

If you want to use Shadow DOM for style encapsulation, you have to include your CSS in a <style> element. The only practical way to do so, at least if you want to avoid FOUC, is to have the CSS in a string in the JavaScript module that defines the custom element.

This runs counter to the performance advice we've been given, which can be summarised as 'less JavaScript, please'. The CSS-in-JS community in particular has been criticised for not putting CSS in .css files, and yet here we are.

In future, we may be able to use CSS Modules alongside Constructable Stylesheets to solve this problem. And we may be able to use ::theme and ::part to style things inside Shadow DOM. But these aren't free of problems either.

3. Platform fatigue

At the time of writing, there are 61,000 open issues on https://crbug.com, the Chromium bug tracker, which reflects the enormous complexity of building a modern web browser.

Every time we add a new feature to the platform, we increase that complexity — creating new surface area for bugs, and making it less and less likely that a new competitor to Chromium could ever emerge.

It also creates complexity for developers, who are encouraged to learn these new features (some of which, like HTML Imports or the original Custom Elements spec, never catch on outside Google and end up being removed again.)

4. Polyfills

It doesn't help that you need to use polyfills if you want to support all browsers. It really doesn't help that the literature on Constructable Stylesheets, written by a Googler (hi Jason!), doesn't mention that they're a Chrome-only feature (edit: this has been fixed after I opened a pull request). The three spec editors are all Googlers. Webkit seem to have some doubts about some aspects of the design.

5. Composition

It's useful for a component to be able to control when (or whether) its slotted content is rendered. Suppose we wanted to use the <html-include> element to show some documentation from the network when it became visible:

<p>Toggle the section for more info:</p>
  <html-include src="./more-info.html"/>

Surprise! Even though you didn't toggle the section open yet, the browser already requested more-info.html, along with whatever images and other resources it links to.

That's because slotted content renders eagerly in custom elements. It turns out that most of the time you want slotted content to render lazily. Svelte v2 adopted the eager model in order to align with web standards, and it turned out to be a major source of frustration — we couldn't create an equivalent to React Router, for example. In Svelte v3 we abandoned the custom element composition model and never looked back.

Unfortunately this is just a fundamental characteristic of the DOM. Which brings us to...

6. Confusion between props and attributes

Props and attributes are basically the same thing, right?

const button = document.createElement('button');

button.hasAttribute('disabled'); // false
button.disabled = true;
button.hasAttribute('disabled'); // true

button.disabled; // false

I mean, almost:

typeof button.disabled; // 'boolean'
typeof button.getAttribute('disabled'); // 'object'

button.disabled = true;
typeof button.getAttribute('disabled'); // 'string'

And then there are the names that don't match...

div = document.createElement('div');

div.setAttribute('class', 'one');
div.className; // 'one'

div.className = 'two';
div.getAttribute('class'); // 'two'

...and the ones that just don't seem to correspond at all:

input = document.createElement('input');

input.getAttribute('value'); // null
input.value = 'one';
input.getAttribute('value'); // null

input.setAttribute('value', 'two');
input.value; // 'one'

But we can live with those quirks, because of course some things will be lost in translation between a string format (HTML) and the DOM. There's a finite number of them, and they're documented, so at least you can learn about them given enough time and patience.

Web components change that. Not only are there no longer any guarantees about the relationship between attributes and props, but as a web component author, you're (presumably?) supposed to support both. Which means you see this sort of thing:

class MyThing extends HTMLElement {
  static get observedAttributes() {
    return ['foo', 'bar', 'baz'];

  get foo() {
    return this.getAttribute('foo');

  set foo(value) {
    this.setAttribute('foo', value);

  get bar() {
    return this.getAttribute('bar');

  set bar(value) {
    this.setAttribute('bar', value);

  get baz() {
    return this.hasAttribute('baz');

  set baz(value) {
    if (value) {
      this.setAttribute('baz', '');
    } else {

  attributeChangedCallback(name, oldValue, newValue) {
    if (name === 'foo') {
      // ...

    if (name === 'bar') {
      // ...

    if (name === 'baz') {
      // ...

Sometimes you see things go the other way — attributeChangedCallback invoking the property accessors instead. Either way, the ergonomics are disastrous.

Frameworks, by contrast, have a simple and unambiguous way to pass data into a component.

7. Leaky design

This point is a bit more nebulous, but it weirds me out that attributeChangedCallback is just a method on the element instance. You can literally do this:

const element = document.querySelector('my-thing');
element.attributeChangedCallback('w', 't', 'f');

No attribute changed, but it will behave as though it did. Of course, JavaScript has always provided plenty of opportunities for mischief, but when I see implementation details poke through like that I always feel as though they're trying to tell us that the design isn't quite right.

8. The DOM is bad

Ok, we've already established that the DOM is bad. But it's hard to overstate what an awkward interface it is for building interactive applications.

A couple of months back, I wrote an article called Write less code, intended to illustrate how Svelte allows you to build components more efficiently than frameworks like React and Vue. But I didn't compare it against the DOM. I should have.

To recap, here's a simple <Adder a={1} b={2}/> component:

  export let a;
  export let b;

<input type="number" bind:value={a}>
<input type="number" bind:value={b}>

<p>{a} + {b} = {a + b}</p>

That's the whole thing. Now, let's build the same thing as a web component:

class Adder extends HTMLElement {
  constructor() {

    this.attachShadow({ mode: 'open' });

    this.shadowRoot.innerHTML = `
      <input type="number">
      <input type="number">

    this.inputs = this.shadowRoot.querySelectorAll('input');
    this.p = this.shadowRoot.querySelector('p');


    this.inputs[0].addEventListener('input', e => {
      this.a = +e.target.value;

    this.inputs[1].addEventListener('input', e => {
      this.b = +e.target.value;

  static get observedAttributes() {
    return ['a', 'b'];

  get a() {
    return +this.getAttribute('a');

  set a(value) {
    this.setAttribute('a', value);

  get b() {
    return +this.getAttribute('b');

  set b(value) {
    this.setAttribute('b', value);

  attributeChangedCallback() {

  update() {
    this.inputs[0].value = this.a;
    this.inputs[1].value = this.b;

    this.p.textContent = `${this.a} + ${this.b} = ${this.a + this.b}`;

customElements.define('my-adder', Adder);


Note also that if you change a and b in the same instant, it will result in two separate updates. Frameworks don't generally suffer from this issue.

9. Global namespace

We don't need to dwell on this one too much; suffice it to say that the dangers of having a single shared namespace have been well understood for some time.

10. These are all solved problems

The biggest frustration of all is that we already have really good component models. We're still learning, but the basic problem — keep the view in sync with some state by manipulating the DOM in a component-oriented fashion — has been solved for years.

Yet we're adding new features to the platform just to bring web components to parity with what we can already do in userland.

Given finite resources, time spent on one task means time not spent on another task. Considerable energy has been expended on web components despite a largely indifferent developer population. What could the web have achieved if that energy had been spent elsewhere?

Posted on by:


markdown guide

I'm sorry, but no, most of the points you defined are extracted from a bad usage of the technology. Then:

1 is not true.
You can progressive enhance web components even with js disabled.

2 is not true.
You don't have to put all the style inside the shadow dom, only the style that should not be composable or accessibile. Just like private ts functions.
You have a lot of flexibility then. I suggest to check this and define the context of your web component.

Another thing, host elements are exposed and fully customizable by external css and you should work on this. Again only the private and functional/structural css should be inside the shadow tree.

CSS-in-JS is not a problem while you write real CSS and generate it inside a css file or embedd it inside a <style> tag. The problem of the first css-in-js tools was the fact that you had to write pseudo css json-like and put it inline. Now, any modern css-in-js solution allows you write standard css and put it inside a separated file.

3 and 4 was true until 2018
Right now all the modern browsers fully support the v1 spec. This mean all the webkit and chromium based browsers (including edge). No polyfill needed for modern development, and if you have to support old browsers you're probably already using polyfill for js things so what's the problem? You just choosed a new technology to develop for old browsers.

5 is normal and it make totally sense.

Slotted elements don't live inside the shadow-tree where you put the <slot> tag, they live in the light-dom, and as any html element in light dom (for example inside the document) they are rendered immediately. Web components are custom html element, if you want to add a lazy behaviour, you have to implement it for your component, like as you do it right now with any lazyload js library.

6 This is true for the whole HTML
These issues are related to HTML and you have them with any framework if you work with plain attributes and not on top of abstractions.

Web components are not React, Vue or Svelte components, they are not logical containers, they are true html elements that live in the document and they should be used like any other html element. Taking a web component and comparing it to an abstracted svelte component is totally misleading. They do different things.

I like sveltejs and i think it's a good framework on top of good abstraction, you don't need to write falsy things about other technologies.

I'm not here to tell web components are the solution or that they are better than other tools. But i don't like content that compare two different things and claim that one is totally bad compared to the other.



Thanks for the comment. We could go back and forth for a long time saying which parts of the other's post were wrong and which were based on misunderstandings, but I'll limit myself to this: if you're saying 'this is just how it is with the DOM/HTML', then you're making my argument for me!


If we bring the topic at this level, you have all of these problem everywhere on the web platform, independently from the tool/framework used. React, Svelte, Vue, Angular... they all work on the web platform, they work with HTML, CSS and JS and share the same platform issues. So web components too share the same issue, but they can't be compared to what you do with React or Svelte. As i wrote here web components are on a lower layer, they aren't an abstraction that generate DOM, they are HTML elements, you write real representation of the DOM (and relative issues) through new html elements, not "logical" components.

Ok, so I'm a bit confused now...

If I understood what you said correctly, then you agree on the fact that web-components are just leaky abstractions built on top of other leaky abstractions, right?

Now, what you are saying is that they can't be compared with React or Svelte, because React, Svelte, Vue... do provide non leaky APIs that "hide" the platform by treating it as an implementation detail.

So, the aim of JS Component libraries is to make "the platform" an implementation detail, because we want non broken abstractions. We want to have clean contracts that allow us to encapsulate functionality, so that we can have composable and reusable things to work with, without having to worry too much about the leaky web-platform. That's (at least in part) why JS Component libraries were created, right?

But then the platform became jealous because we were "hiding" it. So one day it came out and said:

Wait a second! I see that you like to encapsulate functionality and stuff, right? But you don't have to ignore me for doing that! Please, let me create another leaky abstraction for you... Let's call it web-components! Now, please, please, please do not compare them with those things that hide my flaws. Just accept the fact that I'm broken and that I can only produce broken stuff. Just love my new broken abstraction as it is, ok? We all have flaws...

I think that I get it now. You have more compassion for the web-platform than I do. You feel sorry for it.

I have to say that I really respect your unconditional love towards the platform! But I don't share that feeling, at all.


Rich you are on point of fact incorrect on more than half of the article's points. However if figuring this out is not your aim it would naturally lead to back and forth for a long time. Not suggesting this is your aim, or that the points could not be corrected, and in a few cases are widely held misunderstandings. Your comment to back and forth suggests a very low level of interest in actually being open to figuring out how the thing really works so you might reconsider.


1 is not true.
We can't render WCs on the server and WCs not work without JS enabled. Do you ever try to create an isomorphic (universal) web app using WCs? It's the hardest part even if you work with some framework on top of this.

Also, there are many problems with forms, focus/selection, SVGs, etc.

3 and 4 was true until 2018
A problem that polyfills are not the same. One thing when you need to polyfill a simple feature or maybe transpile some new language features. But WCs polyfills always worked bad because this's a thing which hard to polyfill properly. Modern browsers are great and I really love that they're supported WCs well, but we'll never get rid of old browsers.

A simple example close to me - how often you're changing your TVs? Not so often than your phone, right? Our company working with TVs and I know that most TVs in the world (I'm talking about smart TVs of course. Today it's tricky to buy a TV without this feature) comes from 2014-16 years. There are no updates, so seems we'll live with these devices a long time.

5 is normal and it make totally sense.
It makes sense but bad DX. In Svelte 2 we had many problems with this standard behavior.

6 This is true for the whole HTML
True and it's hard to reconcile with it. HTML is really old standard and it was created in a time when no one could ever imagine how we'll use it. Constraints of HTML is the weakest part of WCs.

Writing a whole web app using WCs is too hard and even impossible nowadays. Actually, it's not a choice frameworks vs WCs. The main idea to use WCs as additional HTML elements inside of frameworks templates and it's good. We can name it leaf-components or just html tags. Btw, because of Svelte uses html-first approach, it's very simple and convenient to use WCs in Svelte apps. The main problem today is the majority of existing WCs right now depending on Polymer and it's an ecosystem.

  • 1 You can serve both Custom Elements and built-in extends with the server
  • 3 and 4 are covered by my polyfills that work even on those TVs
  • 5 can be easily simplified
  • 6 you can have tiny abstractions on top and solve most things.

Everything is described in here:

1) Actually, I've no idea what is heresy, but I already read your article. There you show up a very simple case where whole component html representation could be rendered using just props (data-attributes). It's nice, but real cases of web app and especially isomorphic web apps are harder to implement in this way.

WCs logic can be smeared in the whole component. We can't execute WC on the server without some kind of DOM emulation or without using stuff like Headless Chrome (prerender). All these solutions work not really performant.

For example, Svelte, because it's a compiler, can calculate most of the things in buildtime and in SSR mode just concatenate the strings which are very efficient.

3, 4) Perhaps your polyfills work great, but I've used official WC polyfills by Google and it work awful.

5) Your example still too simple to agree with your ideas. Even if we leave DX to the side, in Svelte 2 we lived without scoped slots. So we couldn't bound some parent component state to the slot without proxying it through a top-level component. It was making really complicated creation of the composition of components which should work as one, like Tabs or similar, because of common logic for switching tabs should be in the parent component, but styles and hide/show logic inside each tab component. I mean markup like this:

  <Tab>Tab 1</Tab>
  <Tab>Tab 2</Tab>
  <Tab>Tab 3</Tab>
  <Panel>Tab 1 Content</Panel>
  <Panel>Tab 2 Content</Panel>
  <Panel>Tab 3 Content</Panel>

It's impossible to implement something similar in WC without manual manipulating a slot contents. Check this example: googlechromelabs.github.io/howto-c... It's a exact nightmare.

With scoped slots we can pass a part of the state of component to the child components to give a signal which tab is active now. But unfortunately, WCs doesn't support scoped slots as any other kind of communications between parent and slotted (child) components except direct DOM manipulations which are awful.

6) What do you mean tiny? Lit-html weight is 3.5Kb which is whole Preact. Lit-element already 6.8Kb. Is it still a tiny? Heresy - 8.3Kb, Haunted - 5.1Kb. All gzipped. Are we still talking about tiny abstractions or about frameworks? All these libs based on WCs, so seems most of the work already done, but why they're weighted so damn much? Things like Preact or AppRun includes whole components system but their weight comparable. All these WCs based solutions just solve WCs problems, so seems there're just tons of these problems probably.

1) heresy-ssr is the serer side isomorphic version of heresy. I've been there already with hyperHTML and viperHTML, one of the fastest Hacker News PWAs, if not the fastest, is in viperHTML
3, 4) there is no official polyfill, only one polyfill promoted more than others. The fact Google AMP project itself used my polyfill should already put an end to "the official polyfill" meme: all Custom Elements based projects that succeeded from 2014 used my poly, 'cause it's more compatible, and lighter, and it polyfills builtin extends too.
5) you don't need scoped slots to achieve that, not sure why that use case has to be complicated at all.
6) my libraries have multiple versions: fully polyfilled so no extra anything is needed, only for latest browsers, with ES5 compatible syntax, with polyfills fallbacks to vaporware (the whole ungap.github.io story).
The only reason my libraries are around 5K, but heresy wraps them with extra goodness, which nicely fits in ~2.5K, is that my libraries comes without string attached: all browsers are compatible out of the box, including old mobile, old IEs, etc.
If I could drop every single trick used to fix MS Edge or IE issues, the Safari and Firefox gotcha with Template Literals, and all other quirks I had to fix for every browser here or there, the size would be more like 3K, but then again, as long as any helper that can be used to create with ease tons of components, without ever repeating common patterns, I'm ok in shipping everything included in about 10K and call it a day: that's sill 1/6th of React, if I remember correctly, and the more browsers evolve and fix their internal issues or vanish (IE, MSEdge), the smaller and faster my libraries will become.

On top of that, my libraries requires zero tooling to work via plain standards, that means teams can use these at any time, no toolchain requirements, and I've been in enough teams in my 20 years of programming to value this part almost more than anything else.

Add simplicity, performance, and pretty much everything based on standards, except when it's more convenient doing alternatively, you have the reason my libraries are 5 to 8K, and my poly 1 to 2K. That's the entire payload to unleash all the things the Web platform could do, and beyond (see heresy-ssr, which on cold start, which is the only first time a new template is encountered, is not super slick, but after that, rendering time goes around 0.03 milliseconds, so it's pretty damn good).

1) Ok, so, how you render ShadowDOM on server-side?
3, 4) Maybe I just missed it. Could you please share a link to your polyfill?
5) Could you please describe how can I use WCs to implement these Tabs without scoped slots and manual dom querying in slotted content? I really want to enjoy your solution.
6) Seems, now I know why I not heard about heresy. I see the first release was in April. If it's good as you describe, it should become really popular. So, let's give it some time and will get in touch later to discuss it. ;-)


I assure you that we will be rid of old browsers one day. People said this about IE 5 and IE 6 and when was the last time you supported IE 8?

IE is the slowest to fall out of usage, but eventually it does. Every. Single. Time.

There are plenty of real problems, no need to grasp at straws.

I believe you missed my point about TVs. If someone bought TV in 2017 he'll change it after 10 years, maybe later. So, we'll need to support it in the next 8 years. It's called never.

What? That doesn't make even the slightest bit of sense. Are you saying 10 years is "never"?

It's clearly 10 years.

I mean, 10 years ago we didn’t know what’s SPA, nor Angular, WCs was no trace yet. After 10 years WCs may not be already. 10 years is infinity for web development.

Your perspective is valid, but not complete. Look wider. In 2009 there were things like SPAs, it just wasn't as formalized and the term just wasn't coined yet.

I've been doing web development for 20 years. My career has not been two times infinity years long. If it had I'd think you'd listen to me about this. I'd be effectively God.


This says it better than I did yesterday. Thanks.


I actually don't mind the object/json style syntax so much... I use react-jss via material-ui mostly, and that generates the appropriate stylesheet and adds it to the header. I've played with abstracting it out, but including it in the JS payload works for the applications (not public sites) that I mostly work on.


For some more interesting discussion, this is a good thread:

(The title is me reporting on the sentiment of others, I haven't necessarily made up my mind on all of this)

@dan_abramov 's comment:

A few quick points from my perspective. (I work on React.)

  1. We're not opposed to supporting web components better. The problem is that there's no single "web component community". There are multiple subcommunities. You mention "web component libraries" in the post. These libraries don't agree on a single standard so React would need to pick a side in debates like "how does server rendering work with web components". Whatever we pick will have large downstream effects, and our reluctance to support more has to do with being careful (see [1] note below) about the semantics — not somehow being at odds with web components per se.

  2. As I mentioned in the previous point (and you mentioned in the post), there are multiple "web component libraries". As far as I can see many of the criticisms of React would apply to such libraries as well. I don't think the way to counteract "DOM FUD" is to introduce "library FUD". If you're using a library for defining and updating your web components declaratively, you're not following a conceptually different approach from using React.

  3. Saying "you can do everything with WCs that you can do in React" is double edged. Yes, of course, you can do anything — because we haven't actually agreed upon any constraints. If the constraint is "you don't use a React-like library on top" I think you'll find there's plenty of things that are very hard to do with an imperative abstraction like vanilla WC APIs. We've done a few talks about what using React as a unifying abstraction lets us do (such as non-blocking rendering, or dynamically loading UI code without degrading user experience). You might want to check them out (youtube.com/watch?v=nLF0n9SACd4, youtube.com/watch?v=ByBPyMBTzM0). Of course, you can do these things if you use a library like React on top of WCs. But that negates the argument that you don't need React-like libraries for this.

To sum up: we'd be happy to support WCs better in React. We don't want to rush it, and want to make sure this support is well thought-out. Additionally, we believe there are many things that raw imperative WC APIs don't give you — and for them something like React would be appropriate even in a WC world. Finally, there's this myth going around that once you write React code, you can't reuse it as web components. That's not true — and as you can see from the documentation it's a few lines of code: reactjs.org/docs/web-components.ht...

Hope that makes sense, and provides some additional context!

[1]: For example, if I'm not mistaken, the semantics chosen by Preact make introducing a new standard property to DOM base element potentially a breaking change for the web. We try to avoid such problems if possible — precisely because React did learn from MooTools and we don't want to do another mistake like what happened with Array.prototype.flatten().


This is a good article and I love your work on svelte and with the NY Times.

I got interested in Web Components when working at a large company maintaining several web applications built with different frameworks. It was fun to be able to create separate, lightweight components that could upgrade pieces of different applications without taking on the responsibility of rewriting too much at a time. When we used Web Components we skipped the shadow DOM because of some issues with forms that cross the light and shadow barrier and the weight of the shadow DOM polyfill. We namespaced our component's CSS rules by prefixing them with the custom element name during a build step. Skipping the shadow DOM also allowed us to do server side rendering of our web components relatively easily using jsdom on an AWS Lambda.

I currently use Web Components at a small startup where we create a search widget government websites embed into their pages. We can deliver a small code footprint which encapsulates a ton of functionality with easy integration steps that supports browsers down to IE11.

Neither of these cases refute your points but they are common examples where Web Components shine.


Thank you — yep, there are definitely cases where they shine. It's interesting that you had those issues with shadow DOM — in the early days, shadow DOM was kind of the whole point of web components, but I've seen a lot of people back out for similar reasons. Now we see long-running debates about where and whether to use shadow DOM, and whether a given thing belongs in shadow DOM or light DOM. Feels like that distinction has created a lot of extra complexity.


Shadow DOM is controversial because it plays bad with SSR, and its polyfill is not really specs compliant.

To style Custom Elements you also don't need anything different than you do already, and surely you don't need Shadow DOM at all to have Custom Elements.

Maybe Shadow DOM was sold as the true Web Components must do magic, but since 2014 I've never used it, and never needed it.

We ship Custom Elements to dozen million users, and we never used once attachShadow.

TL;DR the debate about Shadow DOM is pretty simple: don't use Shadow DOM if you target legacy browsers without native Custom Elements V1 support, or use very constrained and simple variants of the spec, like the attachshadow library that works down to IE9.


Shadow DOM is my favorite part, despite its problems. Custom Elements are just interface for me. I get so much use out content distribution. It's the most important part other than style scoping.


I agree with some of the points here about WC implementation but I don’t agree with the presentation.

3) is irrelevant. Spec is and should be a living document so naturally we’ll have to change as spec changes. We should be critical of spec. There is an opportunity to change because it is a living document.

4) I haven’t used a tool in the past decade that didn’t require a polyfill at one time or another. It’s true browsers should implement custom elements uniformly and they don’t, including custom elements v1. Browser vendors could definitely get better at this. I don’t think this argument holds weight if literally any level of sophistication in web development requires a polyfill.

10) Yes, but the nasty side effect is multiple component models leads to division in the greater web community. That’s a problem we don’t talk about enough. We have an opportunity to learn and grow together but can’t when everyday a new library comes out. So instead of Platform fatigue we end up with JavaScript fatigue. I’m not talking about Svelte necessarily, I actually think Svelte is a novel reaction to other JS libraries. The other side effect is no one bothers to learn DOM or vanilla JS because at an early stage in their education they become reliant on the popular JS library. That’s not good for anyone. We end up with a ton of engineers who don’t know how things work under the hood.

8) is so reductive I don’t know where to start. If DOM is bad, then why go into web development? We should just stop everything. Everyone go home. DOM is bad.

Web Components IMHO should have a framework wrapper to make problems like you talk about go away. That doesn’t make them bad, that is the function of every JS library. Make DOM easier to work with. No news there.

Rich, instead of bashing technologies like this perhaps it’s better for everyone if you extol the virtues of Svelte in a context that does address the limitations of DOM, but please frame it in a way that is less divisive.


Web Components IMHO should have a framework wrapper

I guess that's Polymer, but they kinda failed at selling WCs too.

My approach is still Custom Elements based (no umbrella, just the juicy bit), and tiny wrapper to simplify every task mentioned in here.

You can find more in this gist of mine 👋


The moment you need tools to make anything work at all, it's the moment you are already far away from Web standards, and you are allowed to do whatever you want, like in Svelte, and other libraries, case.

Stencil One does not compete with standards anymore, rather with React/JSX, IMHO.

Isn't Stencil just a layer on WebComponents, like Polymer?

Stencil is the best option by far for design systems.

I'd give 🔥 heresy 🔥 a chance too, it works out of the box on client/SSR and since it needs zero real DOM to work, it might easily end up on NativeScript or similar too 🦄

The article keeps saying Stencil compiles to Web Components, that's what I meant 😅

Yeah it's more than that but the final product is WC.

Anyway, frontend has so many tools 😬😬😬😱

hello world example

almost nothing you see in that definition is standard:

  • decorators are not there yet
  • @Prop() first: string; makes literally no sense on JS
  • the JSX return in the render also makes no sense

Stencil One is basically the most hybrid thing of them all, and once it compiles to "standrd JS", it needs a global defineCustomElements to be useful at all.

One can't really ship portable components like this, or can they?

The good news, is that it might target also NativeScript or similar platforms (I'd imagine React native, due JSX in the render), but the bad one is that Stencil One is far away from being a standard based way to develop anything, 'cause it needs, for those parts, mandatory toolchain that is not part of standard Web development.

I hope I've clarified a bit more, happy to answer further, if necessary.

I hope I've clarified a bit more, happy to answer further, if necessary.

Ok got it, I thought that Stencil's output was just pure web components, that's it.

The props and decorators are removed during compilation, and the output is a Web Component. There's a lot of companies building design systems now with Stencil dev.to/ionic/apple-just-shipped-we...


The other side effect is no one bothers to learn DOM or vanilla JS because at an early stage in their education they become reliant on the popular JS library.

I know this is an old comment, but i would like to thank you for recognizing this issue.
I hope more people start thinking about this problem.


I agree with a lot of this, but also feel like the jury is still out on some of it as well. My approach, luckily, is to wade in shallow water and see where things go. I would be nervous about getting too deep into webcomponents.

Though I know this is bound to create debate.


This sounds so much like a framework author saying mine is better, web component and their libraries don't measure up. Again, this points to turf more than facts. Why feel the need to throw trash in someone else's backyard, while praising your own. I've said that svelte addresses solutions be it in it's own way. If you like it, use it. And I make that recommendation sincerely. Not with "I'm not saying you shouldn't use web components, but here's why they're a bad solution.


The headline of this post is 'Why I don't use web components'. And I clearly say that I'm explaining my personal choices. But sure — if you don't think the substantive criticisms I've raised are important, then I'm not going to waste energy trying to change your mind.


I respectfully disagree with most of your arguments. I understand that some of the features of the platform frustrate you. Lets start from the beginning.

1 Progressive enhancement) If you think that websites should work progressively even in browsers without javascript, then web components are not for you. I'd say even the older browsers such as IE are not really suitable for web components. Sure, you can use polyfils but that defeats the purpose. So lets get this out of the way, web components are only meant to be used in browsers that natively support them (and also support javascript).

2 CSS in, err... JS) I gotta say I do agree with problems of CSS encapsulation in Shadow DOM. That is why I don't use the Shadow DOM and its the least favorite feature web components to me. Still I don't mind that it is there, and I'm sure it will be useful in some cases. As for having CSS in files that don't have .css extension... you are free to put your CSS wherever you want, you can also put it in you html file. There is nothing in web components preventing you to do it the old-fashioned way. It is just more convenient to develop in a modular way and have code organized a bit differently even if it means <style> strings in your js. Personally, I like it.

3 Platform fatigue) Totally right. It would be much easier for browser developers if no new features are added. It was much easier when we had Adobe Flash. On the other hand, it was Adobe Flash.

4 Polyfills) Also agree (see point 1). Never use polyfills. Web components should be used only on browsers that natively support it. If you are developing a product that needs to work in IE, do not use web components. Period.

5 Composition) This is not really a web component feature. Its just how DOM works. The problem you solved with svelte, you can still solve using custom elements. Also, this is a problem to be solved on a higher level application framework/library. Alternatively, you can make a custom element that solve this problem for you and then reuse it.

6 Confusion between props and attributes) Again, this is not specific to web components. It sucks and I know. It also sucks that attributes are so expensive in general compared to properties. Personally, I developed my framework to rely mostly on properties and ignore attributes by default because in most cases I don't use them. Sometimes I reflect properties to attributes but only as a CSS selector. Almost never do I listen to attribute changes, but in some rare cases I do. Focusing on properties only, helps with this problem a lot.

7 Leaky design) Not a significant issue.

8 The DOM is bad) It is also good. So is CSS.

9 Global namespace) Since you can only register one element of the same name, I think this kind of makes sense.

10 These are all solved problems) If you really believe that, there is no argument to make you think otherwise. I think it comes down to what you think web applications should be capable of. Personally, I think web should one day be able to do everything that native platforms do except: better, faster, more efficient, with better user and developer experience.

A lot of people see web as a platform for static documents with hyperlinks as it once was while others see it as the future of computation. Where on this spectrum are you? According to your writings, I'd guess somewhere in the middle leaning towards former.


I think you're approaching web components from the wrong perspective. They are not yet complete and as such are not yet suited for architecting a vanilla application. They are, however, very useful in small, reusable cross-framework libraries.

I'll address each of your points:

  1. How much progressive enhancement do you need when the app's JS is either not available (disabled) or still being downloaded? I think that CSS' :defined pseudo-class is sufficient for styling the light tree of an element that has—or will have—a shadowRoot. You can also use a fallback design such as <select is="super-list">.

  2. What "problems" does ::part() have?

  3. Um, yeah, let's not move the web platform forward. Drivel.

  4. No one said that you should use web components for a project that supports IE9/etc. You'd have a ton of polyfills already for that target and probably terribly outdated CSS practices if not using terrible CSS polyfills.

  5. Using existing web components today would still require some form of a wrapper to nicely handle data wiring, and with that, you'd probably have something to handle templating. In which case, this point is a non-issue.

  6. You don't have to write components this way, but it is nice and complete to be consistent with standard elements. See my thoughts at the very top of this comment.

  7. Maybe it will support private methods when that proposal is stage 4. Not a big deal.

  8. See my thoughts at the very top of this comment.

  9. I can see how this could be an issue for architecting an application. However, see my thoughts at the very top of this comment.

  10. They're solved with tools that are not part of an official specification which serves the entire web community. Such frameworks serve opinionated sub-communities. The web component specification has always been taking the best ideas from these frameworks and will continue to do so until we no longer need any of them.


I'm genuinely curious what others are using to solve the real world problem of consuming a design system's components in existing applications that are in various tech stacks? Suppose you're building components to be used in Angular, React, Vue, [whatever hot new framework].

Are you using Stencil to build Web Components? Is Svelte viable?


I'm super curious about this as well.

A few months ago, I worked on a design system project where we faced this question and decided to focus on CSS only, at least to start. We followed Bulma's example and instructed developers to add classes like is-active or is-loading using a framework of their choice. More complicated functionality will probably require web components or something similar - think data tables, date pickers, etc. - but vanilla CSS isn't a bad place to start.

Here are some examples of what other design systems are doing:


I think a major point is that web components are low level API that are unconcerned about where they live. At least with LitElement based web components that we've built. I can't speak for every method used to create them because I believe lit-element and lit-html were built with the goal of eventually making those libraries unnecessary as more gaps get filled in browser specs.


Isn't the push for Web Components mainly coming from one big browser vendor as some sort of "owning the web", as now developers moved on to non-Google tooling ?

I have the observation that the push for some web standards is also a commercial one. If only Chrome moves heavily into that direction and the rest (safari, firefox) is doing it more cautiously we will end up again in a weird situation where:

  • Hype driven developers produce applications only for Chrome,
  • the rest is getting "Move to Chrome" Banners...

It is not proven by data, yet I see many non-googlers sharing doubts about WC vs. Googlers talking about it as the "only good solution".


This is a widley discussed spec. Chrome was just the first vendor implementing the spec v0 and made polyfills. Other vendors refused to implement a draft spec and they all waited for the v1. Now is widely accepted and implemented. No one forced people to use web components v0 or polymer...


As I argued,

If a big Browser vendor who owns the biggest search engine, has one of the largest resources and blogs to push topics, pushes it, people will jump on that.
I used worked on multiple projects in the past with shadow dom v0 , native WCs and polymer.
Developers (Humans) are as much as everyone bound to their Bias, FOMOs etc. so it is not about actively forcing but passively pushing. I think you just undervalue the power of influence and hype in developer communities.

I think hype is just a question of self-control. But you're right, not everyone can remain outside the hype-loop

Including Google themselves. They re-implemented the whole of Youtube with v0 spec which now causes problems in some browsers.

This is no longer true, youtube has already migrated to Polymer v3 which is based on the v1 web components standards so it no longer has any problems with the latest versions of all browsers (except edge but that's already not a problem as long as you use the chromium-based build)


If Google had that much "push" we'd still be using HTML Imports. There's no Google conspiracy, and getting agreement to ship a change is daunting, it's open to public comments and suggestions.


No. Specifications only become recommendations when MULTIPLE VENDORS implement them.

Every major browser vendor gave input to and agreed to Web Components.

The web community and web standards have been working on how to allow developers to extend HTML for at least 20 years.

That is what Custom Elements are.


I mean the whole thing started with MS HTC and FF XBL. So the idea of web components goes way back.


You lost me at "websites should work without JavaScript wherever possible"...
Just wow


Thank you for the thoughtful and insightful article. I hope posts like this can help change the atmosphere of web development and help us build something better.


Rich, I love Svelte, it's really great. Nevertheless, I am a firm believer we should fix issues at their core and not by hiding them or abstracting them under a rug. I haven't worked with WC's but I will say this: The success of Web Components and any Native solution not driven by one single company, is probably not aligned with the interests of the Frameworks and the companies behind them (such as FB) which eventually have an agenda: to push their framework and benefit from the popularity amongst the engineering community, attract developers and eventually convert that power into cash and influence.

It should be obvious to all of us that if we would focus all our efforts in the Platform and stop reinveting the wheel in a series of API changes and rewrites and migrations, we would have a lot more free time to focus on the real reason we are in this job which is to deliver fast, stable, secure, user-friendly interfaces.


Is it possible to inject Svelte components into an existing app without compiling them as web components?


Yep, by default they compile to regular JavaScript classes


Ah ok, we have been looking into using Stencil for some shared components. I assumed you could do this with Svelte but I couldn't find any examples.

But even though Rich built in a way to compile for web components in Svelte, which at least admits the need to address them, he tells you it's not recommended.

It's just a nice to have thing. Svelte is a compiler, so it can just change a target of compilation to WC. But not really many people in the community really use this opportunity.


Please check this example of integration existing Svelte component (Twitter Card widget) to React application created with CRA: github.com/PaulMaly/react-svelte

Example a little bit outdated, but the main idea doesn't change.


It just blows my mind that after all these years, the very obvious problem that you stated yourself: "But it's hard to overstate what an awkward interface it is for building interactive applications."

has not been tackled.
The browser was made to display hypertext media. The DOM was based on that idea.

If we want interactive apps in the browser, why isn't there an alternative to the DOM for interactive apps? Why isn't there a DOMv4?
Why can't we let go of HTML as the outer shell of an application?

We are down to 2 or 3 major browser engines. Time to renovate.


Late to the argument here, but this is the first comment that I understood (not a front-end dev, grokked HTML when it was introduced, backed away for many years..have never used a JS framework/extension/component/whatever or browser API in anger).

It really feels like modern front end development is struggling to escape the document-oriented world of HTML, but has a paranoid fear of shipping code to customers. There have been several attempts in the past to ship portable code under the radar: Shockwave Flash, ActiveX (I lied about portability), Java Applets, Java Web Start, then it seems we got by with ECMAscript and ever-growing numbers of cross-browser APIs, but in that world we're stuck with the DOM, or going it alone in a Canvas?

Perhaps we'll see something akin to the X11/Wayland shift - the old API is too painful and we reach mass adoption of systems that are happy to do everything in OpenGL/3D rendering (three.js and similar are very performant these days), perhaps Web Assembly will help this along.. and folks can finally ship unfettered GUI apps & frameworks that work exactly as they wish - written in their preferred language.. in effect the browser has become a means to ship a runtime (could just as easily be Java, ECMA-335/.NET, WebAssembly, ECMA-262/JS), with a well-defined set of rules to load applications dynamically over a network. It's this last point that makes all the difference between joy and terror for users!

Is it time to re-think the structure of these beasts? Take the network loading rules (almost there with Fetch API I think) and package them up with the chosen runtime, then plug in an HTML renderer on the side, for those few occasions when a plain document arrives?

I'll stop ruminating at this point..


It's totally acceptable as Web Components isn't perfect yet and it's a pretty low level API that can help you solve part of the problems unlike frameworks. But it's totally worth exploring on WC again in the near future. It's a great article BTW.


2: Last I checked, you can use <link rel="stylesheet"... in a Shadow tree.

I like to have @import url("/global.css"); or something similar at the top of any component's stylesheet to implement global themes. A nice and modular solution compared to selectors that penetrate Shadow DOM's.

Edit: Nevermind, I googled FOUC.


I'm really surprised that no one has mentionned anything about tests.

I'm really not familiar with Svelte, it's actually the first time I read about it.
I'm curious and I'm wondering how you would write tests to ensure that the result of the add is right?

  export let a;
  export let b;

<input type="number" bind:value={a}>
<input type="number" bind:value={b}>

<p>{a} + {b} = {a + b}</p>

Web components as they are defined in the spec, to me are just low level tools for others to chime in and do quality libraries/frameworks on top of that that allow shareable end products without having to write wrappers for your next favorite framework out there.
That being said, Stencil does an amazing job with web components I think they have nailed on how to produce web components, not even polymer is that good at the web components stuff in general (lit-html, lit-element are close to it).
Regarding the SSR stuff in there and the JS requirement I'll just leave this here


For 2, I keep my CSS in a CSS file and import it into my component. Also please please use Polymer to create web components. Yes your complaints are all valid it's still not very robust, but Polymer takes away like 80% of that boilerplate code.


I suppose you meant lit-html and lit-element rather than Polymer? :troll:


"I think that websites should work without JavaScript wherever possible."
I stopped reading there. I mean I think babies should work without vaccines or society, but sorry brah, it is not going to happen. Live in the now. JS is a fuckin language interpreted by the browser, which is in turn compiled from some language. It is all code man, why do you insist on the masochistic view that code should "work" without being coded?


Because not all code is the same, obviously? Otherwise why do we have so many languages?

Why are some developers so adamant about TypeScript? Why didn't the web start out with a single scripting language? (Please FFS, do not repeat that canard about "documents"). Why does Markdown exist?

IMO if you don't understand the strengths HTML has that JS can't and vice versa, you should spare some time and effort to doing so.


This may be an increasingly old-fashioned view, but I think that websites should work without JavaScript wherever possible

Ah, you philosophy perfectly aligns with mine, I'm glad there are more prominent people than me who share these sentiments and can argue for our side. I have to check out Svetle now, currently I'm using a weird combo of Turbolinks and self-contained React components :D

  1. Progressive enhancement
    (any-thing tag)fall-back where needed(/any-thing tag)
    use slots, etc for your range of business requirements

  2. Css in, err... JS
    FOUC is simply not true unless a solution did not deliver a working solution that addresses this specifically. Consider continuing to become familiar with the technology in the context of an actual business case. There are various ways to manage these issues, which are more relevant to actual solutions. If the target audience is so far legacy that the solution should use HTML without anything modern, including JavaScript, then by all means forego what is both contemporary and a long-term future for the web platform. However it calls into question why the article was written or relevant more generally.

  3. Platform fatigue
    Actual use of native Web Components does the opposite: reduces the body of knowledge to maintain and allows a focus on fundamentals. Further the platform is becoming more stable over time (years), not less. Simply look at the information for each browser as well as the MDN docs for any given specific or the platform overall.

  4. Polyfills
    I don't understand how using a polyfill is a problem to achieve parity with any framework, feature or otherwise in any actual project. Yes APIs change over time, they evolve, and are needed less and less over time. That's not a case against. There are iterations of React, Angular, etc. If anything it takes all the various moving targets and focuses them, resulting in less work. Less work is good.

  5. Composition
    Consider using a well designed and implemented component rather than a poorly implemented or designed component. It would lead to (at least in my experience) a better solution, less work which I personally see as desirable.

  6. Confusion between props and attributes
    No properties and attributes are not the same. This has been true a long time and am glad we can together discover the technology we're working with. If anything using native technology like Web Components helps address these common deficits and lack of knowledge. This is a helpful exercise toward discovery.

  7. Leaky design
    I don't understand how something "nebulous" or "weird" equates to "leaky". Help me out. It's a callback, not a setter. Please use the features and read the documentation (just Google: MDN attributeChangedCallback).

  8. The DOM is bad
    No the DOM is not bad, it's good and useful and performant. We should all use it directly instead of writing wrapping APIs that do it for us instead, at least until we know what we're doing. Not all the APIs are well designed or necessarily obvious without some work. One of the great thing about native technologies is that they have a more rigorous implementation path with higher standards and experience than simply: is good or is bad. This reveals flawed knowledge across this space. We all have things to learn, I certainly do.

  9. Global namespace
    So? The registry, parsing, etc are prior work and are entirely acceptable. We don't have some long-hand method for element instantiation: new window.EventTarget.Node.Element.HTMLElement.HTMLDivElement(). Custom elements continue all these patterns--it's how they're supposed to work.

  10. These are all solved problems
    Fine. If you don't want the modern iteration of a solution that's totally OK. Enjoy maintaining the various legacy abstractions, build processes perhaps, etc that come with that work.

  1. "I think that websites should work without JavaScript wherever possible."
    I believed in that five, six years ago when mobiles phones didn't supported JavaScript.
    Today, I don't see why not use something that is native in the browsers (even when not all browser works the same)

  2. Seriusly, then why lost time creating HTML5, CSS3 (and later), ES2016+, and the same Svelte. Only use html and your prefered backend

  3. Well, or you implement polyfills, because they are common, or they are traspiled with babel.

  4. 🤦‍♂

  5. Again, if that were the case not even Svelte need to exist. We could simply use vanilla JS (preES6), or if you prefer VB6 or symphony. (just to say a few examples)


Nice presentation. I have to admit you know how to write controversial content.

I feel a bit uncomfortable to put 8. DOM is bad into the perspective of the abstraction that frameworks provide with the native implementation. It might mislead juniors in their understanding.

Worth to mention GlimmerJS to create easily web-components ;)


This is my take on most of the issues... but I threw it up on my phone.

import {$ce} from 'z3util-dom';
class Adder extends HTMLElement {
    connectedCallback() {
        this.createShadowRoot({"mode": "closed"}).append(
            this.#a = $ce('input', {"type": "number"}),
            this.#b = $ce('input', {"type": "number"}),
            this.#p = $ce('p', {})

        this.#a.addEventListener('change', e => {
            this.a = +e.target.value;

        this.#b.addEventListener('change', e => {
            this.b = +e.target.value;

    static observedAttributes = ['a', 'b'];

    get a() { return +this.getAttribute('a'); }

    set a(value) { this.setAttribute('a', value); }

    get b() { return +this.getAttribute('b'); }

    set b(value) { this.setAttribute('b', value); }

    attributeChangedCallback() { this.update(); }

    update() {
        this.#a.value = this.a;
        this.#b.value = this.b;
        this.#raf || (this.#raf = requestAnimationFrame(() => {
            let {a, b} = this;
            this.#p.textContent = `${a} + ${b} = ${a + b}`;
            this.raf = null;

customElements.define('my-adder', Adder);

It's very nitpicky on that point though...


The other cost of complexity imposed by browser standards is the size of the browser. In embedded devices, a large browser means slow load times.

Extra complexity also diffuses development energy, taking away focus on making browsers fast & low memory. It also raises the barriers of entry for new entrants.

Of course, features can be optional. An embedded device with a preloaded application could use a stripped down browser.


I think the fundamental problem is still that we cannot distinguish real market value of a trend VS devloid media. This waste of resources applies to almost everything that is open-source.

An average developer once gets bored of his job and tries to make money out of his or others "invention" (framework) starts blogging-presenting-wrappinginbuzzworkd etc. about it.

This leads to the attention of large companies which use it as a vehicle to attract cheap workforce. Then this big company makes a community to belive that the "invention" is open source and is for greater good.
However, under the hood the community is working on the product of the company that is only somewhat relates to the "invention".
The funniest thing is when the trend is being pushed by another boring member like callback->rxjs for even adding two numbers->lets do toPromise instead->async await! I mean what?

So I think when we realize that something is revolving around such idea we can denote it as a faken-source bullshit which is about the 99% of whats happening right now.

The only problem is with that we are getting vendor-locked in and not in control anymore of what we do and we are forced to spend 50% of our time doing version tracking and updating our applications.

It's like inventing the f*****g hammer each time we hit the nail.


7 is interesting to me. The lifecycle callbacks are stored at definition time, not accessed dynamically when the lifecycle events occur, despite being implemented on a prototype. In other words, CustomElementRegistry.prototype.define’s constructor argument is doing double-duty: it’s both the element constructor and an (unusually nested) options object.

From my POV, this seems like a ‘dishonest’ API. On the other hand, I think I understand the decision: it permits subclasses of custom elements to delegate to the lifecycle hooks of their super class. I wouldn’t have made the same design choice personally, but it is possible to delete the public-properties-that-are-really-private-config-options immediately after definition — the leak can be entirely closed.


I have tried to use Svelte with TypeScript and SASS (two tools that I can't live without). When TS works, SASS won't. When SASS work, TS won't. A lot of git repos promising preprocessors for TS and SCSS, the vast majority won't work. So, useless, for now.