Web Components are here to take over your Frontend, here’s why.

 Web Components are here to take over your Frontend, here’s why.

Artikel uit het frontmania magazine 2022

 

Building frontends using frameworks is writing new deprecated code. That’s my opinion after the last couple of years of building modern frontend applications using web components. Because when you boil all frameworks down to their core features, they all bring the same to the table: building reusable components, making HTML creation and manipulation easy, ensuring encapsulation and doing this in a performant way using (Virtual) DOM engines.

 

Sure, one developer might prefer working with Hooks over a class-based component system. Or one might enjoy working with the simplicity of Vue over the enterprise-ready approach that Angular takes. Though at the end of the day, they’re all for the same purpose: allow developers to rapidly build interactive webpages using a well-thought-out component system. And that’s why I advocate for using web components: a set of tools for building modern frontend applications with zero dependencies by default.

 

{ Web Components 101 }

‘Web components’ are a term you might have heard more and more about in the last couple of years. What started as an obscure browser API is now a serious contender for frontend developers around the world when they pick a component development system for new applications. Big tech companies like Google, Adobe and Microsoft are cashing in the profits of using web components. Even in the Dutch institutions like ING, Rabobank and the Police are actively working on frontend applications based on them.

 

The use case for web components in reality is quite simple. Imagine this: you want to build a dialog using the <dialog> element, but want to add a bunch of custom code specific to your scenario. When investigating how to do this, you’re left with two obvious solutions: use a framework you know and love, or go vanilla. Going vanilla is often perceived as a negative thing: writing spaghetti code without a proper component architecture plan, manually chaining together event listeners or even fall back to an old classic: jQuery.

 

But what if you could make a <fancy-dialog> which extends the existing native one with your custom functionality? And if it was based on a component class with proper lifecycle hooks just like the framework du jour? And work out of the box in any modern browser, with no NPM or build tooling required? That’s where web components come in.

 

The term ‘web components’ does not actually refer to a singular technology, but rather a suite of browser standards. They allow you to create custom elements on the page, to live alongside the native elements you know and love. This is achieved by utilizing three powerful browser standards: ‘Custom Elements’, ‘Shadow DOM’ and ‘HTML Templates’. Each of these three standards are a piece of the puzzle that makes web components as powerful as they are.

 

{ Custom elements }

Custom elements are the bread and butter of web components. Like the name implies, using this standard you can define your own elements on the page with a custom HTML-tag, like <fancy-dialog>. The only requirements: an element needs to be a class extending from HTMLElement, and it needs to have a name with two or more hyphenated words so the browser can make a distinction between native and custom elements. A basic custom element looks something like this:

 

class FancyDialog extends HTMLElement {

constructor() {

super();

this.innerHTML = `<some html to render>`;

}

// Other implementation details like handling events

}

customElements.define(‘fancy-dialog’, FancyDialog);

 

After execution, using the element on the page is as simple as placing a <fancy-dialog> tag anywhere on the page. The last line of code is the most critical one: it tells the custom element registry that the FancyDialog class should be instantiated when the defined tag is encountered. From that point on, the element behaves like any other native element present on the page. It’s a node in the DOM tree, events bubble like you would expect and you can easily query or style them using the selector syntax you already know. And there’s two added bonuses: you now can build components on the page similar to using components in any other framework, plus it all runs natively in the browser without hundreds of megabytes worth of toolchain in your node_modules directory.

 

{ Shadow DOM }

Custom elements by themselves are cool, but rendering HTML using this.innerHTML isn’t exactly doing anything for encapsulation. That’s where the Shadow DOM comes in: possible the coolest sounding browser standard out there. To best describe the Shadow DOM, it’s best to illustrate the concept with a diagram (see Image 1.)

 

Image 1.

 

On any node in the DOM, you can upgrade the node to become a shadow host. From that point on, you can interact with the newly created shadow root and treat it like you would with any other element on the page. You can use browser APIs like the query selector or appendChild to add content to the shadow DOM inside of the host node. The browser then takes the completed tree and merges it into the Light DOM – the DOM tree you normally use when building a webpage. The added benefit: anything that happens inside of the Shadow DOM stays in the Shadow DOM.

 

You can add styling nodes inside of the Shadow DOM with extremely selectors such as h1 { color: red; } and the styles will only be applied within the separated tree structure. Events bubble like you would expect them to, with the added benefit of having more control on whether events can bubble outside of the shadow tree. The accessibility tree also works like it would normally: the shadow tree is flattened into the main DOM tree and assistive technologies can walk through the nodes like they normally would.

 

The nice thing is: you’re already using the Shadow DOM. When you enable user agent Shadow DOM in your developer tools settings, you can actually inspect the implementation details of native elements such as the range input shown in Image 2.

 

Image 2.

 

Encapsulation is all about hiding irrelevant implementation details from the outside, and making sure the inner workings of a component don’t leak outside of its context. The Shadow DOM provides true encapsulation in a native way, without any of the weird workarounds that (Virtual) DOM engines use to create virtual encapsulation using tricks like CSS-class suffixing.

 

{ HTML Templates }

The only piece of the puzzle left for a robust and performant component system is a way to efficiently create and update HTML on the page. Luckily for us, there’s a native way to easily achieve this goal using HTML Templates. Usage is simple: create a <template> element on the page, and add some other elements and styles inside. When encountered by the parser on the page, it will parse and validate the contents, but does nothing with the element. Nothing is shown to the user, media URLs are not fetched and scripts are not executed. But when required, template elements can instantly be cloned and placed in parts of the DOM tree. Because the browser provides the low-level implementation for this behavior, it’s extremely fast.

 

Together, these three standards form what we call ‘web components’. When you combine these standards, you can build components using a class-based component system, you can easily truly encapsulate the inner workings of a component, and you can performantly manage HTML templates using the template element. And the browser support? It’s fantastic! Because these are web standards, every major greenfield browser supports them. And fear not, even for legacy browsers there’s a plethora of polyfills available to run web components smoothly

 

{ Libraries & ecosystem }

It’s entirely possible to create web applications using just the native browser APIs. Using them makes me feel like I’m ten years old again: creating an index.html file, adding a script tag and boom! Stuff magically appears on the page, and it required minimal effort. But when you’re looking at web components in the context of a company, it’s not just a matter of finding a technology that works. There are all sorts of extra requirements, like the surrounding community & ecosystem, whether you can recruit developers familiar with the tech, and whether the developer experience is up to par with popular frameworks.

 

When it comes to the developer experience, it’s entirely possible to create an easy-to-maintain codebase using just vanilla JavaScript. But you’ll need to set up your own basic building blocks: figure out routing, your own way of managing styles on the page and set up your own build/bundling configuration. To make it a little bit easier, there’s a handful of libraries available which take away the heavy lifting from you while still using browser standards under the hood. The most well-known libraries are:

  • Lit
  • Stencil
  • Angular Elements

 

They all take a radically different approach. Lit is all about staying as close as possible to a vanilla custom element, while Stencil and Angular Elements are all about providing a batteries-included and enterprise-ready setup. At the end of the day though, all elements inherit from HTMLElement and fully interoperate with each other on the page. My personal approach when picking a technology is to start with vanilla, upgrade to Lit if necessary and only try the others if Lit does not cut it for you. The reason for this is that the Lit source code is simple to understand, there’s zero build toolchain requirements and there’s no additional dependencies you pull in.

 

Looking further at the surrounding ecosystem, there’s some great developments happening in the web components sphere. Open Web Components (open-wc.org) has some great resources available for common set-ups and questions regarding tooling. Rocket (rocket.modern-web.dev) gives you a zero-JS server side rendered toolchain similar to Astro. Vaadin (vaadin.com/components) has many enterprise-grade components available for all your grid and chart needs. And even the ING bank has open-sourced all their core white-label components in Lion (github.com/ing-bank/lion) so you can easily add your own styles and have a flexible and fully accessible design system. These examples are just the tip of the iceberg: there’s a pretty active community on Twitter continuously sharing cool new developments!

 

{ When (not) to use them }

Looking at the ecosystem as a whole, it has grown sizably and became much more professional in the last few years. Finding out that even Photoshop in the browser was built using web components (a technical marvel regardless of technology choice) made me realize it’s ready for production and a serious contender for all frontend applications.

 

There are some cons of course: it’s hard to find experienced developers that have worked with web components. It can be jarring to have to write your own router when the ones available on NPM don’t match your specific requirements. And depending on whether you picked a library or went full vanilla, you’ll find few blogposts online helping you decide on best practices.

 

But when you look past those points, you’ll find it’s very easy nowadays to build a modern frontend application using just browser standards. Shipping complex applications with near-zero dependencies while maintaining full browser compatibility is nothing short of revolutionary. So, it’s time to pull up your sleeves. Develop your frontend application using web components and free yourself from the eventual sunsetting of your favorite framework!

 

Bio

Wessel Loth is Tech Lead at Arcady and member of the Frontmania Magazine editorial committee.