Skip to main content Accessibility Feedback

The elevator pitch for Web Components

I’ve worked with Web Components a little bit over the last few, but really struggled to understand the use case for them.

Until this week.

Between Jeremy Keith’s article on HTML Web Components, plus using one for a client project with NASA, something just clicked in my brain finally.

I’m now convinced that they’re the best way to author DOM manipulation libraries.

Over the last few days, I converted half a dozen old JS libraries of mine to Web Components. I also created a new Web Components collection on the Lean Web Club, where you can download or copy/paste ready-to-use Web Components into your projects.

Over a Mastodon, I’ve been getting some questions about why I think this approach is better, and how it’s meaningfully different from new MyLibrary() JavaScript instantiation and [data-some-library] selectors.

Today, let’s talk about that!

The elevator pitch

Here’s what makes Web Components really shine over traditional JavaScript libraries…

  1. You get scoping and instantiation for free out-of-the-box.
  2. You get built-in methods that detect when the element enters or leaves the DOM, and when attributes on it change.
  3. Options and configs are declarative (in the HTML), not just JS. That makes it them both easier to author and easier to use differently in various places in the HTML.

Let’s look at each of these in a bit more detail.

Scoping and instantiation

With a traditional JavaScript constructor library, you need to instantiate each instance of your component in the DOM.

Let’s say you have two tab navigation sections on the page. To run your script, you need to manually instantiate each one.

<nav id="about-us">
	<!-- ... -->

<nav id="our-products">
	<!-- ... -->
new Tabs('#about-us');
new Tabs('#our-products');

With Web Components, simply including the custom element in the HTML instantiates it.

	<!-- ... -->

And because Web Component classes happen on the HTMLElement base class, this inside your class methods is automatically scoped to the custom element.

That makes it just a little bit easier to scope your event listeners, selector methods, and so on to the Web Component itself.

constructor () {

	// Get all of the toggle nav linkns
	let toggleLinks = this.querySelectorAll('a');


You absolutely can do the same thing with a traditional JavaScript library. I have!

But as someone whose written a lot of DOM manipulation libraries, the built-in scoping makes things a lot easier to author.

Built-in helper methods

Web Components include the connectedCallback(), disconnectedCallback(), and attributeChangedCallback() methods.

These run when a custom element enters the DOM, leaves the DOM, or an attribute on it changes, respectively.

If you have content that gets dynamically rendered, it’s really nice to have built-in methods for detecting when it enters or leaves the UI. They provide useful hooks for instantiating tasks when an element is loaded, and cleaning things up when it leaves the DOM.

Detecting attribute changes means you can do cool reactivity stuff with your component.

For example, the browser-native details element displays its contents when the open attribute is added to it. The dialog element is displayed in non-modal form if you add open to it.

The attributeChangedCallback() method lets you do similar things with your custom Web Components.

Declarative options and configs

If you want let users customize Web Component behavior, the way you do it is through HTML attributes.

<!-- Standard ToC -->

<!-- Only use h2 headings -->
<table-of-contents levels="h2"></table-of-contents>

<!-- Change the heading for the ToC -->
<table-of-contents heading="On this page..."></table-of-contents>

<!-- Combine settings -->
<table-of-contents heading="On this page..." levels="h2, h3"></table-of-contents>

This makes it really easy to include multiple instances of a Web Component in your HTML, and use slightly different settings and configurations for each one.

Because the options are all configured declaratively with the HTML, you can tell just from looking at the element what’s going on. And it becomes much easier for people who aren’t JavaScript experts to work with the library.

People who’s focus is more on HTML and CSS can be much more productive with this kind of API than with a JS-based one.

Compare that way of configuring your library to something like this…

<div data-toc="first"></div>
<div data-toc="second"></div>
<div data-toc="third"></div>
new TableOfContents('[data-toc="first"]', {
	levels: 'h2'

new TableOfContents('[data-toc="second"]', {
	heading: 'On this page...'

new TableOfContents('[data-toc="third"]', {
	heading: 'On this page...',
	levels: 'h2, h3'

Now, you could setup a traditional JavaScript library to also support declarative options using data attributes. I’ve done that in the past, too.

<div data-toc="first" data-levels="h2" data-heading="On this page..."></div>

It adds a fair bit of complexity as a library author, though.

You generally still want to support JS-based configurations, so you’re merging default settings, JS-based options, and declarative options together. You also need to decide how to deal with conflicts if there’s a declarative option and JS-based option set for the same setting.

Getting comfortable with Web Components

If you want to really dig into how to work with Web Components, I have 17 lessons, a workshop, a handful of projects, and a brand new set of copy/paste components over at the Lean Web Club.

Today through Monday, you can join for less than $5/month, or just $45 for a whole year!