DEV Community

Émile Perron
Émile Perron

Posted on

Web components in 2021: the Good, the Bad and the Ugly

Web components are a native set of features that provides outstanding scoping of styles and functionalities. They can be used in a regular, framework-free web page, but also with any Javascript framework of your choice (e.g. React, Vue, Angular, Svelte, etc.). This makes web components excellent for building reusable elements that need to be shared publicly or reused across multiple projets. At least, in theory.

In reality, there are some drawbacks that can make web components almost unusable in some projects.

In this article, I will explain what makes web components great, what their drawbacks are, and I will provide some guidance to help you decide whether or not you should use them in your projects.

The Good

The two main features that make web components powerful are the Custom Elements API, and the Shadow DOM.

The Custom Elements API is what allows you to create and register your components as new HTML elements. It also allows you to define lifecycle callbacks for your new element. Overall, it is pretty great and fairly simple to understand and get into, for both novice and experienced developers alike.

The Shadow DOM is what provides all of the encapsulation of styles. It gives your components their own DOM, which is separate from the rest of your document. This means that global styles cannot affect it (except for CSS custom properties / variables), and that its own styles cannot affect other elements in the parent document.

The HTML <template> and <slot> elements are also used in most custom elements, allowing you to easily create templates with dynamic content without having to reach for a third-party templating system or language.

Browser support for all of these features is great: unless you're still supporting Internet Explorer, you're unlikely to run into any deal-breakers. There is one exception to this, which will be explained later in "The Bad" section.

Plus, as mentioned at the start of the article, not only are web components compatible with just about every Javascript framework out there, but they can also be used in good old vanilla Javascript, without a framework. That's because web components are basically just ES6 classes extending the native HTMLElement. That means you can share components across your project or your company's entire ecosystem.

Additionally, there are some great libraries and packages to make building web components easier, as well as an online platform where you can find and share web components with others:

The Bad

Flash of Unstyled Content

Let's start with the Custom Elements API. The only drawback I have experienced with custom elements is the potential for a Flash of Unstyled Content. Since custom elements are declared and registered in Javascript, it can take a few milliseconds for them to be loaded, processed, registered, and finally rendered. While this is happening, your custom element are left unstyled or hidden.

This could be a major drawback for a marketing website where you have just a few seconds to engage with your visitors in order to keep their attention, but in web applications, it's not really a deal-breaker, especially since your browser's cache dramatically dampens the issue after the initial loading.

Here's an example of FOUC with a "tabbed container" web component on a reload without cache (on a local development server):
Web component flashing unstyled content for a few milliseconds before rendering correctly

Here is the same component rendering on reload, with browser cache (still on a local development server):
Web component loading immediately without any visual glitches or FOUC

As you can see, browser cache makes this a non-issue for repeat visits.

Shadow DOM doesn't play well with native forms

The biggest issue I have encountered with web components is the fact that they do not play well at all with native form functionalities. This is due to two things:

  1. Custom elements cannot extend elements other than HTMLElement (without tedious workarounds and major drawbacks);
  2. Form elements inside the Shadow DOM are not considered as such by the component's parent form.

Remember how the Shadow DOM doesn't use global styles? This means that if you want to use a <form> inside a web component, you will have to re-define the styles of every <input>, <select>, <textarea>, <button>, <label>, <fieldset>, and more, within your component's styles.

Of course, you could make each of these elements their own web component, so they each encapsulate their own styles. However, because form elements such as HTMLInputElement cannot be extended by custom elements, your custom input component has to include the <input> in its Shadow DOM. And this is where you run into the next issue: inputs (and other form elements) within the Shadow DOM are not considered part of the form.

For example, if a form's submit button is inside the Shadow DOM, the form cannot be submitted by pressing Enter inside an input anymore, unless you add your own keydown event listener to replicate this feature yourself.

Here is another example that is a little more complex and telling. If you want to make a custom input, you have three solutions:

  • You can generate a <input type="hidden"> in the regular DOM, beside your custom element, and manually replicate a bunch of built-in features to ensure your input is synchronized correctly at all times, triggers the right events, is validated correctly, is accessible, looks good, and works well.
  • You can make every form element, including the <form> itself, its own web component, and forego native <form> elements for your entire project.
  • Handle every form who uses this custom input element with Javascript

If you are already in a Javascript-heavy environment, where every single form is handled via Javascript, and every component implementation already requires a lot of work in order to be usable and accessible, this might not seem like a major issue.

However, if you are more vanilla-oriented, newer to web development, or simply like simple solutions and environments, this is likely to be a MAJOR deal-breaker.

A non-negligible percentage of the web components I would like to build are meant to work with forms or form elements in one way or an other, and I expect it is the same for most other developers.

The Ugly

The worst part is that there isn't much information on the web about what is being done to fix or circumvent this issue of incompatibility with native forms.

Web component libraries like Shoelace simply implement their own custom form element, which must be handled manually in Javascript.

Librairies that aim to help build web components, such as Google's Lit, cannot allow extending built-in elements because Safari doesn't support customization of built-ins.

Where we stand, and whether should you use them

Overall, just a few weeks/months after embarking on my web component journey with a big smile and sparkling eyes, I find myself not pessimistic, but slightly disappointed about the current state of web components and their future outside of Javascript framework projects and ecosystems.

I still believe the idea and general implementation of web components is great. But the drawbacks when it comes to native forms make them much less easy to learn and to implement into.

You should use web components...

  • if you're already handling all your forms manually in Javascript
  • if you have (or plan on having) multiple projects or ecosystems with different technology stacks in which you need to share/reuse components
  • if you don't mind taking a lot of time to reimplement built-in functionalities and accessibility before you can really start working on your own business-related features (or if you can use an existing component library like Shoelace to save on the initial development time and costs)
  • ... or if you don't need your components to interact with forms or form elements

You should not use web components...

  • if you want to retain the ability to use native forms
  • if you need to support legacy browsers

A light in the distance

Just after I initially published this article, @westbrook commented to let me know about the ElementInternals spec which is currently implemented in Google Chrome (not in Safari or Firefox yet however). Once available in every browser, this could be a valid solution to the form-related problems I mentioned in the article.

Check out the following articles to learn more about this spec, its implementations, and the available polyfills:

One last thing...

If you are not in a Javascript-heavy environment but would still like to use web components for your forms (e.g.: you're building a Laravel or Symfony web app), you always have to possibility of developing a universal form handler to overcome the problems that are described in this article.

Sure, it's more complicated than just using native forms, and it will require you to do some more development and testing before you can get started, but it's probably the simplest workaround.

If you do have any other workaround or solutions in mind, I'd love to see them here in the comments or on Twitter.

Top comments (19)

westbrook profile image
Westbrook Johnson • Edited

Émile, have you gotten a change to checkout, it's a pretty well agreed to spec with in-progress (except maybe in Safari, as with too many things 😞) development or shipped implementation in various browsers. It's a great way to ensure that custom elements are properly associated with the forms in which they live. @calebwilliams12 also has a great write up on this for CSS Tricks where he introduces a polyfill for smoothing those browsers that have yet to fully catch up: I think it'll do great things for form processing, and the underlying Element Internals API is opening up a lot of other cool possibilities in accessibility as well, so it's worth a follow. Cheers!

emileperron profile image
Émile Perron

Wow, thanks for your comment, I wasn't aware of this spec!

Although it's quite as "simple" as allowing the extension of built-in form elements, it looks like it's the next best thing! Plus, this allows for way more customization for components that do not extend/represent built-in form elements.

I'll definitely give this a shot and keep track of the browser adoption.

Thanks again! :)

westbrook profile image
Westbrook Johnson

Agreed about extensions of built-ins, it would be really nice if Safari would just get over that and ship it, but it's better to move on than to fume on it too much at our level, even if there's a relatively small polyfill for them: We should look at trying to get it separated/removed from the web components spec so that it's easier to do so, IMO. Then, we can focus on more agreeable/likely to ship x-browser specs like Element Internals.

Once you've had some time with Form Associated Custom Elements, I hope you'll share updated thoughts on the path forward with web components so we all can check them out!

Thread Thread
emileperron profile image
Émile Perron


I will experiment with the Form Associated Custom Elements in the next few days/weeks, and I'll update this article or make a new one about my thoughts and experience if it's worthwhile!

Thanks again for all this great information 🙌

Thread Thread
emileperron profile image
Émile Perron

@westbrook , you really helped big time with these comments today.

I just toyed around for 10-15 mins while reading the CSS-Tricks article you mentioned, and that was enough time to successfully turn my globally-styled inputs into a working input web component.

I expect I have a ton more work ahead to ensure it's as accessible and fully-featured as a regular input, but it is great to have a working and supported(-ish) solution to this problem!

I'll definitely make another article about this once I get enough time in. 👍

christianulbrich profile image
Christian Ulbrich

While in theory the form-associated API is the right™ choice, its current support is limited (only Chrome). However it is not so hard, to support Submit, Reset interaction of CustomElements. You simply grab your closest() <form> (and of course piercing any ShadowDOM you might encounter) and then simply listen for Submit or Reset events and act accordingly:

  • reset your components to some initial state for a reset
  • add your hidden inputs mirroring your serialized data to the DOM of the aforementioned form either on submit or, simply synchronize them on every change
dannyengelman profile image
Danny Engelman

One point to add.

Web Component technology is older and more used than most people know.

The <video>, <audio>, <textarea>, <input> and many more complex HTML tags
are implemented in each Browser with Web Component (like) technology.

In Chromium you can tell by the user-agent (= tech term for Browser) label on the shadow-root:

Us mortal developers can not access the HTML elements inside user-agent shadow-root.

Also see:

But... that technology was not available to us 3rd party developers

What we now call "Web Components" (Custom Elements API, Templates, shadowDOM)

IS that very same technology available to us all

But we can't access user-agent managed shadowDOM! (or "closed") shadowRoots)

So everyone who claims Web Components is young, not yet fleshed out, bare metal, hardly used, technology... does not understand how the Browser works.

emileperron profile image
Émile Perron

It's true that the underlying technology isn't new, and has been out there and in use by browsers for a rather long time.

However, usage by the masses in their own websites and applications is different than internal usage by browser developers, so it's not untrue that the technology, to the eyes of the public, is fairly young and hardly used. I think that'll change in the future though.

dannyengelman profile image
Danny Engelman • Edited

When does hardly used then gets to the next stage?

Says: "over 10% of Chrome browsed websites, use ``customElements.define"

And that is with (about) half of all Social Media "Web Components" posts,
partly focussing on the "bad" aspects.

And if your stats are based on what Web Components you detect in F12 Dev Tools...

You could be wrong, because active Web Components could have done their work and removed themselves:

fjones profile image
FJones • Edited

WebComponents are one of those things where we're getting toys before they're ready. In principle, they allow for excellent encapsulation and reusability. In practice, the overhead (including the amount of basic infrastructure needed to develop them) makes them tedious to work with, and the rules around them aren't really practical in many ways. Some bleed-through of CSS is often desired. Some better way of transporting state in and out of them and interacting with the current document/window natives is often desired. Some means of filling placeholders that well exceeds <template> (e.g. DOM nesting restrictions) is often desired.

It's a simple API on the surface, but to make it really applicable at scale it needs a lot more thought.

christianulbrich profile image
Christian Ulbrich

You seem to have not really used Web Components, they are offering all what you are mentioning today:

  • basic infra -> and go bundleless
  • "bleed-through" of CSS -> either use CSS custom properties or Shadow Parts
  • transport all your glorious state, like you are used to know using either attributes, properties or projected content via default slots; i.e. you have everything at your hand, that the DOM offers
  • placeholders -> slot fallback content
  • DOM nesting restrictions? / template -> Huh? Use slots and / or nested slots
emileperron profile image
Émile Perron

The work that has been put in these past few years, especially by the community, has made them quite usable in many scenarios. And from what I've seen from the articles mentioned by @westbrook just a few minutes ago, they will likely get much better in the not-so-distant future.

However, I have to agree that there is stil a lot of work to do to before they can really get widespread adoption. They will likely be much more developer-friendly, permissive and scalable in the future.

claviska profile image
Cory LaViska

It’s worth mentioning that many framework users bind data directly to form controls (vanilla and in shadow roots). If you’re doing that you won’t run into the form data problem.

+1 regarding ElementInternals, though. I can’t wait for a proper form participation API so this becomes a non-issue.

dam profile image

You can avoid FOUC by using a shared css file that is first loaded by the page then linked inside the WC template.

The browser will have already cached the shared styles so it will Not reload the stylesheet, but instead use the cached version. Linking the stylesheet in the template adds the shared styles to the WC's scope.

emileperron profile image
Émile Perron

There's something to be done with assets preloading to help with the FOUC, indeed.

However, it seems you can still run into FOUC issues when your components make use of the shadow DOM, as the structure of the element will likely change between the initial page load and the moment that the WC is rendered.

nuwannnz profile image
Nuwan Karunarathna

Nice article! I have used LitElement to develop a very complex Chrome extension and so far the performance and the development experience has been great for me.

emileperron profile image
Émile Perron

Glad you liked it! I also use Lit, and the ease of development and performance is indeed quite impressive!

ptorran profile image

An interesting insight into web components. Wonderful article!

andremarcondesteixeira profile image
André Marcondes Teixeira

You should not use web components as well, if you need different styles of the components depending on where they are used (In other words, if you want your components to use global css)