However, web components have a number of drawbacks. For instance, they have a number of technical limitations and are easy to misuse in a way that excludes users.
It’s possible—and certainly my hope—that web components will improve over time and these issues will be resolved. But for now, I’m holding fire on them.
In this article I’ll explain why that is, and suggest an alternative way to develop components in the meantime.
They are constraining
In his criticism of web components, Michael Haufe explains that:
- custom CSS pseudo selectors can’t be used with web components
- they don’t work seamlessly with native elements and their associated APIs
- if we wanted to create a custom button, for example, we can’t extend the HTMLButtonElement directly, we have to extend the HTMLElement
Additionally, web components have to be defined with ES2015 classes which means they can't be transpiled to give more people the enhanced experience.
So, straight off the bat, there are a number of technical constraints to work around when it comes to using web components.
They are not widely supported
Currently, web components have relatively poor cross-browser support, so the enhanced experienced won’t work for everyone.
That doesn’t mean we can’t use them, it just means we’ll need to provide a baseline experience that works for everyone else. That’s progressive enhancement.
But we should think seriously about whether the choice to use web components is the most inclusive option. If we don’t use web components, we can provide the same rich experience to a significantly wider group of people. I’ll explain how later.
Polyfills offer a way to provide broader support. But they are slow, unreliable and hard to work with in general, and have a number of specific limitations when used to make web components work more broadly.
So while it may be preferable for us as code authors to use standards-based technologies, it’s not necessarily beneficial to our users—which should always be our first priority.
They are easily misunderstood and misused
By the same token, it can encourage people to make components that request their data with AJAX and render themselves, like little iframes.
This type of approach causes a number of avoidable issues which I’ll explain by way of an example.
Imagine we want to load a table showing the sales figures for a product our website sells using AJAX like this:
<table> element for this exact purpose and it works everywhere.
To make our table work in these situations, we would need to put a
<sales-figures>. This is known as graceful degradation.
<sales-figures> <table>...</table> </sales-figures>
If the component already has a populated table on the page when the page loads, wrapping
<sales-figures> around it gives us and our users nothing.
Finally, using AJAX can introduce a number of usability and accessibility issues.
- AJAX is often slower than a page refresh, not faster.
- We’ll need to create custom loading indicators, which are usually inaccurate and unfamiliar to users, unlike browsers’ loading indicators.
- We’ll need to make AJAX work cross-domain, which isn’t straightforward.
- As the components load the page will jump around causing visual glitches and potentially making users click the wrong thing. You may have heard about skeleton interfaces as a way to solve this problem. They are placeholders put where the components will end up being shown once loaded. But while they help a bit, they don’t fully solve the problem because they can’t always predict the exact size of the content that will load.
- Point 4 affects screen reader users too because they won’t know whether the components have loaded, have failed to load or are in the process of loading. ARIA live regions provide a way to communicate these states to screen readers. But when several components are being loaded, the user will be bombarded with announcements.
Scale this up to several web components on a screen and we risk giving users a very unpleasant, exclusive and slow experience to contend with.
Components that depend on AJAX requests to the server are no longer framework agnostic and therefore interoperable. This somewhat defeats the object of using web components, given that interoperability and technology agnosticism are 2 of the main benefits they aim to provide.
Importantly, none of these problems are the fault of web components per se. We could easily develop components to work like this without web components. But, as demonstrated, it’s easy to misinterpret web components and unknowingly use them in a way that hurts both users and code authors.
They are hard to compose
Let’s say we have just 2 web components. One for sortable tables and another for expandable rows.
<sortable-table> <table>...</table> </sortable-table> <expandable-rows> <table>...</table> </expandable-rows>
But if we want a sortable table with expandable rows then we need to nest the components like this:
<expandable-rows> <sortable-table> <table>...</table> </sortable-table> </expandable-rows>
The relationship between
<table> is unclear. For example, it’s hard to tell whether
<expandable-rows> is operating on the
<table> or the
The order matters, too. If each component enhances the table it could create a conflict. Also, it's not clear which component initialises first—the inside one or the outside one.
(Note: you may have heard about the
is attribute as a way around this but Jeremy Keith explains that browsers aren’t going to implement this in extensible web components.)
They can’t just be dropped into an application
One of the supposed benefits of web components is that we can drop one script per component onto the page and they just work—regardless of the application or tech stack.
But unlike standard elements, we may need to add additional code to get them to work properly. In some ways this is a bit like adding a framework or library.
One example of this is polyfills which I mentioned earlier. If you choose to use a polyfill to provide broader support, then that code needs to be ready and waiting in your web page.
This is usually fixed by adding a script in the
This is perhaps of little consequence overall, but it does considerably negate one of the supposed benefits of using web components.
Framework agnostic components without web components
You may have heard web components being sold as an alternative to using frameworks.
While I’m in favour of creating interfaces without client-side frameworks, this is misleading for a number of reasons.
Firstly, client-side frameworks usually provide additional features besides enhancing pieces of the interface.
Secondly, web components can be used in tandem with frameworks.
By creating components like this we can avoid the drawbacks I’ve described in this article.
Let’s use the same sortable table and row expander to do this.
RowExpander classes inside.
SortableTable.js // define SortableTable class and behaviour RowExpander.js // define RowExpander class and behaviour
Once that’s done, we can initialise the components like this:
// grab table var table = document.querySelector('table'); // initialise sortable table var sortable = new SortableTable(table); // initialise row expander var expander = new RowExpander(table);
We can make these components fire events just like web components. Something like this:
sortable.addEventListener(‘sort’, fn); expander.addEventListener(‘expand’, fn);
Web components hold a lot of promise because they give code authors a way to create interoperable components based on standards.
As a result, it should be easier to understand other people’s code and create components that can be reused across projects.
But even if we choose to provide enhancements exclusively for cutting edge browsers that support them, there’s still several limitations and issues we need to tackle.
Huge thanks to Amy Hupe who not only edited this article from top to bottom, but also made it as simple and inclusive as possible. Not an easy feat for an article on web components of all things. 🙌
Sign up to Good Design
I'll send you one email per month about nailing the basics, avoiding complexity and making things work for everyone.