My approach to HTML web components

I’ve been deep-diving into HTML web components over the past few weeks. I decided to refactor the JavaScript on The Session to use custom elements wherever it made sense.

I really enjoyed doing this, even though the end result for users is exactly the same as before. This was one of those refactors that was for me, and also for future me. The front-end codebase looks a lot more understandable and therefore maintainable.

Most of the JavaScript on The Session is good ol’ DOM scripting. Listen for events; when an event happens, make some update to some element. It’s the kind of stuff we might have used jQuery for in the past.

Chris invoked Betteridge’s law of headlines recently by asking Will Web Components replace React and Vue? I agree with his assessment. The reactivity you get with full-on frameworks isn’t something that web components offer. But I do think web components can replace jQuery and other approaches to scripting the DOM.

I’ve written about my preferred way to do DOM scripting: element.target.closest. One of the advantages to that approach is that even if the DOM gets updated—perhaps via Ajax—the event listening will still work.

Well, this is exactly the kind of thing that custom elements take care of for you. The connectedCallback method gets fired whenever an instance of the custom element is added to the document, regardless of whether that’s in the initial page load or later in an Ajax update.

So my client-side scripting style has updated over time:

  1. Adding event handlers directly to elements.
  2. Adding event handlers to the document and using event.target.closest.
  3. Wrapping elements in a web component that handles the event listening.

None of these progressions were particularly ground-breaking or allowed me to do anything I couldn’t do previously. But each progression improved the resilience and maintainability of my code.

Like Chris, I’m using web components to progressively enhance what’s already in the markup. In fact, looking at the code that Chris is sharing, I think we may be writing some very similar web components!

A few patterns have emerged for me…

Naming custom elements

Naming things is famously hard. Every time you make a new custom element you have to give it a name that includes a hyphen. I settled on the convention of using the first part of the name to echo the element being enhanced.

If I’m adding an enhancement to a button element, I’ll wrap it in a custom element that starts with button-. I’ve now got custom elements like button-geolocate, button-confirm, button-clipboard and so on.

Likewise if the custom element is enhancing a link, it will begin with a-. If it’s enhancing a form, it will begin with form-.

The name of the custom element tells me how it’s expected to be used. If I find myself wrapping a div with button-geolocate I shouldn’t be surprised when it doesn’t work.

Naming attributes

You can use any attributes you want on a web component. You made up the name of the custom element and you can make up the names of the attributes too.

I’m a little nervous about this. What if HTML ends up with a new global attribute in the future that clashes with something I’ve invented? It’s unlikely but it still makes me wary.

So I use data- attributes. I’ve already got a hyphen in the name of my custom element, so it makes sense to have hyphens in my attributes too. And by using data- attributes, the browser gives me automatic reflection of the value in the dataset property.

Instead of getting a value with this.getAttribute('maximum') I get to use this.dataset.maximum. Nice and neat.

The single responsibility principle

My favourite web components aren’t all-singing, all-dancing powerhouses. Rather they do one thing, often a very simple thing.

Here are some examples:

  • Jason’s aria-collapsable for toggling the display of one element when you click on another.
  • David’s play-button for adding a play button to an audio or video element.
  • Chris’s ajax-form for sending a form via Ajax instead of a full page refresh.
  • Jim’s user-avatar for adding a tooltip to an image.
  • Zach’s table-saw for making tables responsive.

All of those are HTML web components in that they extend your existing markup rather than JavaScript web components that are used to replace HTML. All of those are also unambitious by design. They each do one thing and one thing only.

But what if my web component needs to do two things?

I make two web components.

The beauty of custom elements is that they can be used just like regular HTML elements. And the beauty of HTML is that it’s composable.

What if you’ve got some text that you want to be a level-three heading and also a link? You don’t bemoan the lack of an element that does both things. You wrap an a element in an h3 element.

The same goes for custom elements. If I find myself adding multiple behaviours to a single custom element, I stop and ask myself if this should be multiple custom elements instead.

Take some of those button- elements I mentioned earlier. One of them copies text to the clipboard, button-clipboard. Another throws up a confirmation dialog to complete an action, button-confirm. Suppose I want users to confirm when they’re copying something to their clipboard (not a realistic example, I admit). I don’t have to create a new hybrid web component. Instead I wrap the button in the two existing custom elements.

Rather than having a few powerful web components, I like having lots of simple web components. The power comes with how they’re combined. Like Unix pipes. And it has the added benefit of stopping my code getting too complex and hard to understand.

Communicating across components

Okay, so I’ve broken all of my behavioural enhancements down into single-responsibility web components. But what if one web component needs to have awareness of something that happens in another web component?

Here’s an example from The Session: the results page when you search for sessions in London.

There’s a map. That’s one web component. There’s a list of locations. That’s another web component. There are links for traversing backwards and forwards through the locations via Ajax. Those links are in web components too.

I want the map to update when the list of locations changes. Where should that logic live? How do I get the list of locations to communicate with the map?

Events!

When a list of locations is added to the document, it emits a custom event that bubbles all the way up. In fact, that’s all this component does.

You can call the event anything you want. It could be a newLocations event. That event is dispatched in the connectedCallback of the component.

Meanwhile in the map component, an event listener listens for any newLocations events on the document. When that event handler is triggered, the map updates.

The web component that lists locations has no idea that there’s a map on the same page. It doesn’t need to. It just needs to dispatch its event, no questions asked.

There’s nothing specific to web components here. Event-driven programming is a tried and tested approach. It’s just a little easier to do thanks to the connectedCallback method.

I’m documenting all this here as a snapshot of my current thinking on HTML web components when it comes to:

  • naming custom elements,
  • naming attributes,
  • the single responsibility principle, and
  • communicating across components.

I may well end up changing my approach again in the future. For now though, these ideas are serving me well.

Have you published a response to this? :

Responses

Joe Gregorio

In My approach to HTML web components Jeremy Keith goes into a naming convention for both web components and their attributes.

While I don’t have any opinions on attribute naming, I do have a strong opinion on element names, and that’s if you decide to namespace your elements it should be done using a post-fix and not a pre-fix.

As an example, all the elements we’ve build in Skia Infra are post-fixed with -sk:

https://jsdoc.skia.org/theme/theme-chooser-sk-demo.html [Source]

 checkbox-sk collapse-sk error-toast-sk icons-demo-sk multi-select-sk nav-button-sk nav-links-sk radio-sk select-sk spinner-sk styles tabs-panel-sk tabs-sk toast-sk

This is in contrast to prefixing, which makes the names much harder to read, such as all the iron- elements in Polymer:

https://www.webcomponents.org/collection/PolymerElements/iron-elements

 iron-image iron-selector iron-localstorage iron-label iron-collapse iron-checked-element-behavior iron-demo-helpers iron-scroll-target-behavior iron-a11y-keys iron-form-element-behavior iron-meta iron-media-query iron-validatable-behavior iron-pages iron-jsonp-library iron-ajax iron-list iron-doc-viewer

To me the former are much easier to read, while the latter just reads like “iron iron iron iron” when used on page.

stephband

@adactio Hello. Talking of Custom Elements and The Session, I have discovered that CSS Grid is a near-ideal music notation layout algorithm. I write about that here:

https://cruncher.ch/blog/printing-music-with-css-grid/

I turned that idea into a custom element.

https://github.com/stephband/scribe

It’s not ready for production, but here’s a test render of Sí Bheag Sí Mhór, whose ABC I lifted from The Session:

https://stephen.band/scribe/si-bheag-si-mhor.html

No, it’s not perfect. It’s work-in-progress. But I’m pretty amazed at how far Grid takes us.

Cruncher

# Posted by stephband on Tuesday, April 30th, 2024 at 2:27pm

21 Shares

# Shared by godrin on Monday, April 29th, 2024 at 3:44pm

# Shared by Kevin Marks on Monday, April 29th, 2024 at 4:12pm

# Shared by Ramón Corominas on Monday, April 29th, 2024 at 4:12pm

# Shared by Zach Leatherman :11ty: on Monday, April 29th, 2024 at 4:12pm

# Shared by Baldur Bjarnason on Monday, April 29th, 2024 at 4:13pm

# Shared by Lewis Cowles on Monday, April 29th, 2024 at 5:11pm

# Shared by Tanquist on Monday, April 29th, 2024 at 5:12pm

# Shared by Dave 🧱 :cursor_pointer: on Monday, April 29th, 2024 at 5:12pm

# Shared by Wojciech Wernicki on Monday, April 29th, 2024 at 5:36pm

# Shared by Mark Conroy on Monday, April 29th, 2024 at 8:17pm

# Shared by Tyler Sticka on Monday, April 29th, 2024 at 8:17pm

# Shared by Derek on Monday, April 29th, 2024 at 9:18pm

# Shared by Lene Saile on Monday, April 29th, 2024 at 9:47pm

# Shared by Peter Janes on Monday, April 29th, 2024 at 10:47pm

# Shared by Ironorchid on Tuesday, April 30th, 2024 at 12:15am

# Shared by Aslak Raanes on Tuesday, April 30th, 2024 at 7:27pm

# Shared by Simon MacDonald on Wednesday, May 1st, 2024 at 2:38am

# Shared by 🍄🌈🎮💻🚲🥓🎃💀🏴🛻🇺🇸 on Wednesday, May 1st, 2024 at 3:12am

# Shared by Timo Tijhof on Wednesday, May 1st, 2024 at 11:48pm

# Shared by fosstodon.org on Saturday, May 4th, 2024 at 12:34pm

# Shared by Brian :python: :flask: on Saturday, May 4th, 2024 at 12:34pm

32 Likes

# Liked by Aslak Raanes on Monday, April 29th, 2024 at 3:44pm

# Liked by Morbi on Monday, April 29th, 2024 at 4:09pm

# Liked by Seth A. Roby on Monday, April 29th, 2024 at 4:09pm

# Liked by Kevin Marks on Monday, April 29th, 2024 at 4:10pm

# Liked by Jason Neel on Monday, April 29th, 2024 at 4:10pm

# Liked by Ramón Corominas on Monday, April 29th, 2024 at 4:10pm

# Liked by Arri Blais :verified_trans: on Monday, April 29th, 2024 at 4:10pm

# Liked by Steffen on Monday, April 29th, 2024 at 4:11pm

# Liked by Zach Leatherman :11ty: on Monday, April 29th, 2024 at 4:11pm

# Liked by Baldur Bjarnason on Monday, April 29th, 2024 at 4:12pm

# Liked by Lewis Cowles on Monday, April 29th, 2024 at 5:11pm

# Liked by Dave 🧱 :cursor_pointer: on Monday, April 29th, 2024 at 5:11pm

# Liked by Piper Haywood on Monday, April 29th, 2024 at 5:35pm

# Liked by Aaron In Iowa on Monday, April 29th, 2024 at 5:35pm

# Liked by Paul Robert Lloyd 🐢 on Monday, April 29th, 2024 at 6:40pm

# Liked by Chris Smith on Monday, April 29th, 2024 at 8:14pm

# Liked by Geoff on Monday, April 29th, 2024 at 8:14pm

# Liked by Tyler Sticka on Monday, April 29th, 2024 at 8:16pm

# Liked by Derek on Monday, April 29th, 2024 at 9:18pm

# Liked by Oliver Schafeld on Monday, April 29th, 2024 at 9:46pm

# Liked by Lene Saile on Monday, April 29th, 2024 at 9:46pm

# Liked by Juan Fernandes on Monday, April 29th, 2024 at 10:19pm

# Liked by Peter Janes on Monday, April 29th, 2024 at 10:19pm

# Liked by Tom Anypuppies on Tuesday, April 30th, 2024 at 7:04am

# Liked by Warren Buckley on Tuesday, April 30th, 2024 at 7:29am

# Liked by Zachary Dunn on Tuesday, April 30th, 2024 at 11:56am

# Liked by 🍄🌈🎮💻🚲🥓🎃💀🏴🛻🇺🇸 on Wednesday, May 1st, 2024 at 3:12am

# Liked by Chris Coyier on Wednesday, May 1st, 2024 at 3:12am

# Liked by Luke Harby on Wednesday, May 1st, 2024 at 6:27am

# Liked by Frank // Mottokrosh on Wednesday, May 1st, 2024 at 8:27am

# Liked by Timo Tijhof on Wednesday, May 1st, 2024 at 11:48pm

# Liked by pvergain ⏚ (framapiaf) on Friday, May 3rd, 2024 at 6:54pm

1 Bookmark

# Bookmarked by Brent Lineberry on Tuesday, April 30th, 2024 at 8:35pm

Related posts

Announcing Web Day Out

A one-day event all about what you can in web browsers today: Brighton, March 12th, 2026. Tickets are just £225+VAT!

Train coding

Generating a static copy of The Session from the comfort of European trains.

Preventing automated sign-ups

Here’s a bit of PHP I’m using on The Session.

Manual ’till it hurts

Try writing your HTML in HTML, your CSS in CSS, and your JavaScript in JavaScript.

Pickin’ dates on iOS

Mobile Safari doesn’t support the min and max attributes on date inputs.

Related links

Interop Feature Ranking

This is a nifty initiative:

This site lets you rank the proposals you care about, giving us data we can use when reviewing which proposals should be taken on for 2026.

For the record, here’s my top ten:

  1. Cross-document view transitions
  2. Speculation Rules API
  3. img sizes="auto" loading="lazy"
  4. Customizable/stylable select
  5. Invoker commands
  6. Interoperable rendering of HTML fieldset/legend
  7. Web Share API
  8. CSS scroll-driven animations
  9. CSS accent-color property
  10. CSS hanging-punctuation property

Tagged with

Close to the metal: web design and the browser

It seems like the misguided perception of needing to use complex tools and frameworks to build a website comes from a thinking that web browsers are inherently limited. When, in fact, browsers have evolved to a tremendous degree

Tagged with

Snook Dreams of the Web - Snook.ca

If we were to follow Jiro’s and his apprentices’ journeys and imagine web development the same way then would we ask of our junior developers to spend the first year of their career only on HTML. No CSS. No JavaScript. No frameworks. Only HTML. Only once HTML has been mastered do we move onto CSS. And only once that has been mastered do we move onto JavaScript.

Tagged with

Building WebSites With LLMS - Jim Nielsen’s Blog

And by LLMS I mean: (L)ots of (L)ittle ht(M)l page(S).

I really like this approach: using separate pages instead of in-page interactions. I remember Simon talking about how great this works, and that was a few years back, before we had view transitions.

I build separate, small HTML pages for each “interaction” I want, then I let CSS transitions take over and I get something that feels better than its JS counterpart for way less work.

Tagged with

5 Questions for Jeremy Keith · Frontend Dogma

If you like the prospect of an old man ranting at clouds, this is for you.

Tagged with

Previously on this day

10 years ago I wrote 100 words 038

Day thirty eight.

17 years ago I wrote Open Audio

Take my audio file. Please.

19 years ago I wrote A quick theological question

Riddle me this.

19 years ago I wrote How to Be a Web Design Underwear Pervert

The presentation by the two Andies has been brought into line with pending trademark applications.

20 years ago I wrote North to Alaska

Jessica and I are flying to Seattle tomorrow. We’ll spend the weekend with her brother, Jeb, taking in the sights and sounds.

23 years ago I wrote Acquire, Manage, Listen

So we’re finally starting to see some sanity in the great “music biz vs. the rest of the world” conflict.