When Should I Use a Web Component?

ยท

13 min read

The biggest tech companies with the biggest websites are using Web Components and yet very few frontend engineers know how to build them and even fewer know when. I think it's because developers aren't sure where Web Components can fit in the crowded world of frontend technologies. Let's change that.

Building on the web for 13 years working at all kinds of companies on all kinds of projects using pretty much every frontend tech available has taught me four compelling cases for using Web Components:

  1. Design Systems

  2. App Shell aka Platform Components

  3. Leaf Elements

  4. 3rd-party Components

To be clear, this is not about what you can create with Web Components - you can make any kind of component as a Web Component - but rather when using Web Components is better for your architecture and overall engineering strategy.

Clarifying outdated perceptions...

I want to quickly clarify something about the use of Web Components. From my perspective, proponents of Web Components in the past tried to market the technology as a way to create widgets like an image carousel, for example, and publish them to be shared all over the internet. I do not ascribe to this idea. In fact, I think such a portrayal has actually harmed Web Component adoption because very few commercial web projects are interested in those kinds of off-the-shelf UI widgets. The assumption is - and it's a correct one - that these "community" widgets won't match the website's design language (or will be difficult to customize), don't have all the features your website would need, are often of poor quality, and your developers have little to no control over the maintenance of these widgets. So, for some years now Web Component as a technology choice was dismissed as undesirable or not relevant to commercial products.

I believe these misconceptions are changing. Professional teams are starting to build Web Components and their implementations are exclusive to their projects. That's how I do it and that's the context in which I'll be sharing these four cases for building Web Components. Let's begin.

Design Systems

Web Components can enable a design system to be used on virtually every website ever created. You could use it on a website written 20 years ago and you could use it on a website written 20 years from now.

Take a second and let that stew if you find it hard to believe. It's not the age of websites that matters, is it? Let's see how this impacts the cost of a design system project.

Design System ROI

HTML is forever, so as long as the browser is newer than 2018 (meaning it supports Web Components) then a design system built with Web Components will work even on a website that's older than you.

From a cost perspective, no other tech can match the ROI of a design system built with Web Components.

This best-in-class ROI is influenced by even more factors than longevity alone.

There's familiarity. Nothing will be more familiar and easily understood by developers than HTML, and that's what they get from a design system built with Web Components. It's the easiest tech to introduce to all of your engineers. Working on the design system code is also more approachable. At work, we have a new design system that is 100% vanilla HTML, CSS, and JavaScript. As a result, anyone should be able to jump in and make edits.

There's also speed, as in page speed. No framework can match the execution speed of native web APIs. Native is the benchmark. Web Components are the only way to get native performance; everything else adds overhead and sometimes it's significant overhead. Improved page load time and time to interactivity are also secondary effects of using Web Components as they produce a much smaller design system that doesn't take as long to download and activate. I've personally shrunk a 300kb React design system down to an impossible 7kb! It's all about leverage, see the TAC CSS methodology if you want to learn this unique approach.

And finally there's the adoption rate of a design system, which is the most critical factor of ROI. Developing a design system is pure cost until other teams adopt it. As adoption increases design systems become a major force multiplier and it's Web Components that enable the highest possible adoption rate within an organization. The only other option is to force tech stack standardization to just one framework and attempt to maintain that across all teams and all projects for all time. That's an unnecessary and detrimental constraint at scale, so just use Web Components instead to remove all barriers to adoption past, present, and future.

State-of-the-art

Design systems are in fact where Web Components have seen the most use because they are the premier technology choice.

Google's Material, perhaps the most popular design system, is actively releasing a new version built with Web Components. So is Microsoft with their Fluent UI project. Many other huge design systems have already upgraded to Web Components, namely IBM's Carbon, Saleforce's Lighting, and Adobe's Spectrum. Some lesser known design systems were built with Web Components from their inception (Clearly the smartest engineers in the room or just old boring web standards diehards. Or does the latter make you the former ๐Ÿ˜œ).

So, if you're going to build a design system or are considering a refactor, use Web Components. A Web Component design system will last the longest, be the most familiar, run the fastest, and achieve the most adoption.

App Shell aka Platform Components

Next are platform components and for the same reasons as design systems, Web Components are ideal for this use case.

What are platform components?

These are domain-specific pieces of UI used across multiple products. Things like the app header, sidebar, and footer - the app shell - are common examples. But platform components include much much more than that.

Let's look at GitHub's UI and see what other examples we can find...here's one:

The "Single sign-in" prompt shows up in many places across the site whenever you're not signed in to your organization. No doubt this component gets used by more than one team at GitHub and none of those teams are its creator (the identity team probably made it). This is a solid Web Component candidate!

Here's another:

This search input is found all over GitHub. It's another solid candidate for using a Web Component (again, for all those exact same reasons explained in the design system section above).

In fact, if you inspect the DOM you'll see <qbsearch-input>... That's a Web Component! The engineer who made it could have drank a six pack of framework and belched out another React component but they didn't. Someone at GitHub is making healthy engineering choices!

Both of those are small components, but size doesn't matter. The entire GitHub site header could absolutely work as a Web Component. For that we would build <gh-appheader> instead of <AppHeader> which, like a framework component, can absolutely include other Web Components, like the <qbsearch-input>. Thinking about and designing a component structure is really no different with Web Components than it is with component-based frameworks. You'll really start to see that as you gain more experience (and you'll wonder why you didn't do this sooner!).

Everything about the ROI of Web Components and design systems applies to platform components. It is the fastest, smallest, most familiar and readily-adoptable option. When it comes to platform components, use Web Components.

Leaf Elements

Let's now turn away from using Web Components for shared UI and look at a use case where we would use them for chunks of app-specific UI.

What is a leaf element?

Let's think about the DOM for a minute. Imagine a DOM tree and follow it all the way to the end of each branch and you will see the leaf elements, things like an <h1> and <button> and <input>. These elements are what the user actually sees and interacts with, and this is true whether you are writing vanilla HTML or using a framework. Ultimately, HTML elements and only HTML elements are created and added to the DOM no matter what tech stack you're using.

So, do you always need a middleman to get you there?

A typical website design will include many pieces of custom UI that are effectively leaf elements, i.e. they are small distinguishable features of the interface. Said another way, the whole website is of course not a leaf element and neither is a page or even a section of a page. But those smaller parts that make up a section of a page are what I refer to as leaf elements.

Some leaf elements are quite small and might actually result in a single HTML element out at the tip of the DOM tree, and some are bigger and encompass a small branch of DOM with several leaf elements. The size isn't really what matters, what we're really looking for are those parts of the UI where the framework, say React, is no longer necessary. Those custom bits of UI are very strong Web Component candidates because at this point in the DOM the framework has finished its proprietary work. We can turn these chunks of DOM into a single leaf element.

Got that? It's clear as mud!

Let's go back to GitHub and try to see if we can find some examples.

Looking at the bottom half of the profile page we find this contribution section:

There's a lot here: a calendar, menus, year list with active year, timeline of activity, and more. Our goal is to identify the invisible lines around pieces of this UI that do not need or benefit from the framework. These lines mark the beginning of a Web Component. They're like the dead ends in the network of streets that make up your app's component neighborhood.

Here's the two dead ends I see:

The contribution calendar and year list should be Web Components because nothing about them needs a framework, whereas the contribution activity UI does. The activity UI is quite fragmented and would benefit from using a framework to sort of glue it all together. So, I could imagine if this was a React app it would have the following component structure:

// From a high level we have the Profile page component
function ProfilePage() {
  return (
    <div>
      <gh-appheader /> *Hey, it's our platform Web Component!*
      <main>
        <Sidebar />
        <PinnedRepos />
        <Contributions />
      </main>
    </div>
  )
}

// And here's the Contributions section component
function Contributions() {
  const [year, setYear] = useState(null);
  const contributions = useContributions();
  const counts = contributions.reduce(...);

  function handleYearSelect(e) {
    // CustomEvent has a detail property for event data
    setYear(e.detail.year); 
  }

  return (
    <div>
      <gh-contributioncal counts={counts} /> *Web Component*
      <Activity contributions={contributions} year={year} />
      <gh-yearlist onSelect={handleYearSelect} /> *Web Component*
    </div>
  )
}

Just like React stops at the boundary of a native <select> element it also stops at the boundary of our <gh-yearlist> element. We still use React to interface with these elements, like binding to their events.

Why build apps this way?

There are philosophical and practical reasons to build leaf elements with Web Components (and using more vanilla HTML too!).

The philosophical is pretty straight forward: if you care about web standards and progressive enhancement then you believe in leveraging the web platform. You use fetch not Axios. You use modern CSS features not SASS. You use Web Components not a framework. You have a craftsman mindset.

The practical reasons for using Web Components include:

  • Built-in support for scoped styles

  • Scoped element ids

  • Slotable content

  • Works with SSR

  • No tooling needed

  • No extra dependency to manage

  • Faster, more responsive UI that's "closer to the metal"

  • The technology has a forever shelf-life

  • Web Component skills are universally transferable

  • Improves the overall architecture and maintainability of the app

That last one is huge.

Imagine a codebase you work on regularly... How tightly coupled is all that code to the project's dependencies?

And how many dependencies are there? I've worked on web apps with more than 60 runtime dependencies and more than 130 developer dependencies.

If you could quantify it, would you say 50% of the code requires one or more dependencies in order for it to work? Maybe 70%? I've seen projects where 80% or more of all code is directly tied into at least one of the project's numerous dependencies. This creates so many problems as dependencies age and diverge and when things need to change in the project. These balls of intertwined code are the most expensive part of software development. It's difficult to code, harder to test, harder to debug, and even harder to understand and harder to refactor.

Look at this app (this is real btw):

Thousands and thousands of edges.

Let's zoom in:

That's overwhelming and impractical to even try to understand, so I'll run an analysis on just one small page of this app. Now we're only seeing one page:

This page is just a few charts and some pretty basic content and minimal interactions and yet there is almost 100 components and imports (the blue ovals). And this is just the imported modules, it doesn't show the actual DOM nodes.

Is this an extreme example? Yes, but it isn't much different than other projects I've worked on. Your app's dependency network probably looks the same. This has become the status quo for modern web applications. If you're comfortable with this then you...well, I'll pray for you.

Finding the leaf elements in your UI and using Web Components to build them will drastically simplify the network of imports. Here's what that same page looked like after I did that:

Yes. This is real. That's the same page's imports after I turned leaf elements (the chunks of UI that don't really need a framework) into Web Components and more vanilla HTML. The number of custom and third-party modules used by this page shrunk from almost 100 to 5 (React itself would make 6).

Should you keep using a framework? Yes, but use it less. Use it to glue together more and more vanilla HTML and Web Components. I've seen codebases shrink by half following this approach!* Every aspect of projects that leverage Web Components improves - writing the code, refactoring code, building the project, testing it, loading and interacting with it, debugging it - every bit of it improves.

*I have done this several times and right now I am refactoring a large website built with Next.js (I literally just stepped away from my work computer doing that work). By refactoring this wesite to use more native HTML and Web Components we have deleted over 6,000 lines of code. It will be close to 10,000 by the time we're done.

3rd-party Components aka Plugins

Website plugins, like Stripe's Payments Element or Intercom's chat widget, are one more case where using a Web Component makes the most sense:

Conflicts with customer's tech

Using a Web Component avoids the potential for framework conflicts or version mismatches with your customers' tech stack.

Plugins like these often prevent customers from updating to newer versions of their chosen framework because a 3rd-party plugin is slow or unable to release compatible updates. This is a lose-lose situation for both parties and can be avoided only with Web Components.

Better investment

Like the ROI on design systems built with Web Components, organizations stand to gain a lot more from building plugins as Web Components. It's the difference between investing in just one plugin that works with all customers vs. dumping more money into multiple framework-specific versions and even missing customers that use a technology you haven't built support for.

Best bet for performance

Web Components are the lightest and fastest option. Your widget will get ejected from a customer website if their developers find it's interfering with their page's performance (I have done this more than once).

Many widgets of old did avoid framework-specific implementations for obvious reasons, but they were all built with various less-than-ergonomic APIs and their source code was based on some homegrown ideas. Web Components help sort all that out by offering customers a familiar standards-based API and your developers can follow a clear documented pattern for creating these widgets.

The Future of Web Components

Popularity of Web Components is growing and will skyrocket with the release of React 19 (version 19 fixes issues React had that prevented developers from incorporating Web Components in their React apps and because I'm such a gentleman that's all I'm going to say about that).

The biggest names on the web are shipping Web Components in their apps. They've not only learned how to build them - that's the easy part - but as time goes on they're learning more and more when we should use them.

And now you know!

Oh, one more thing. When building Web Components please don't start by installing more stuff from NPM, even stuff that promises better Web Components. Go to MDN and learn it for yourself: https://developer.mozilla.org/en-US/docs/Web/API/Web_components

ย