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 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.
<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.
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: webcomponents.org.
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.
As you can see, browser cache makes this a non-issue for repeat visits.
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:
- Custom elements cannot extend elements other than
HTMLElement(without tedious workarounds and major drawbacks);
- 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
<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.
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 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.
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.
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.
- 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
- if you want to retain the ability to use native forms
- if you need to support legacy browsers
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:
- More capable form controls (web.dev).
- Creating Custom Form Controls with ElementInternals (CSS-Tricks)
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.