Web Components: The vanilla framework

Today we begin a new series of articles about the world of Design Systems. We'll start by talking about one of the biggest and most revolutionary standards released in the last decade: Web Components.

Web Components are a group of APIs that allow users to define their custom reusable components in a web standard-based way.

In this post, we will touch more on the theoretical side.

First, let's talk about how we got here.

Introduction

A common practice carried out (although not as often as developers would like) by the official organizations, like W3C or TC39, is to standardize those features, approaches or architectures that have become popular and heavily used over the time, have dev community support and cover or solve features not implemented by the standards yet.

We have a lot of examples we could talk about, but just to mention a few:

  • The jQuery element selector was standarized as querySelector() and querySelectorAll() methods
  • Most features provided by MomentJS were standardized or are being standardized in the I18n official API
  • The variables provided by CSS preprocessors like SaSS were somehow implemented with the CSS vars
  • Module management was implemented by different projects, like CommonJS or AMD, but the one that became the de facto standard was RequireJS. Finally, it was also standardized with the ES6 release by TC39.

Web Components went through something similar. The components paradigm is an approach adopted by popular frameworks like AngularJS, Angular and React years ago with the strong support of the development community. AngularJS implemented the components' approach with directives, Angular since v2 while React from the beginning.

Then, the W3C along with the TC39, released a set of APIs that affect both HTML, CSS and JS and that allows building custom reusable components. These APIs are:

  • Custom elements
  • HTML Templates
  • ShadowDOM
  • ES Modules

The APIs

As described above, the Web Components specification is not a single feature or even a single API. The Web Components specification is currently 4 APIs. All of them, especially custom elements, HTML templates and ShadowDOM are related to each other.

These 4 APIs are the ones currently implemented in v1 spec. It's important to point out that the specification is continuously evolving and other APIs were defined in v0, such as HTML imports, although they were abandoned in the end. Also, there are APIs being developed to be included in the future, such as HTML Modules. We can keep track of all these updates on the official Github repository.

Next, we'll see in detail what each API consists of.

Custom Elements

The custom elements API is the one that allows us to define custom HTML tags (and therefore, DOM elements) and their attributes.

In order to not confuse them with native tags as well as not collide with them, it is mandatory to use at least two words and a hyphen when defining them.

<my-profile name="Marty" lastname="Mcfly"></my-profile>

There is also the possibility to extends the functionality of existing current HTML elements, also known as customized built-in elements, but this isn't implemented by all modern browsers (for instance, Safari decided not to implement it so far).
Currently, it's still considered an experimental feature. You can check the current browser support in Can I Use. This is something we'll see in future posts when we talk about scaling and extending web components.

HTML Templates

The HTML Templates API is the one that allows us to define HTML code to be reused and could be the markup of our web component.

These templates, defined with the <template> tag, have the advantage of not being loaded during the initial page loading, therefore, not rendered or added to the DOM. Inner scripts, images and audio won't be executed, loaded or played until we'll use them from JS. The browser engine only analyses the code in order to check that the template content is valid.

<template>
  <div class="profile-picture">
    <img src="marty.png" alt="Marty Mcfly" />
  </div>
  <div class="name">Marty Mcfly</div>
</template>

Although templates are not loaded, they can be queried using selector functions, like for instance querySelector() or querySelectorAll.

Slots, represented by the tag are another type of HTML templates. Slots allow the author of a web component to define which content will be customizable by the consumer with his own HTML. We'll see more about slots in the next section of this article when talking about Shadow DOM.

Shadow DOM

The Shadow DOM API is probably the most important one. This API is the one that brings us encapsulation for markup as well as for styles. This means that our web component code and styles will not overlap with the rest of the elements of the page where the component belongs to. The encapsulation applies both from the web component to outside as well as from the page to inside the web component. To do so, an independent DOM subtree (shadow DOM) is attached to the main DOM.

To illustrate:

  • If we execute a document.querySelector()we won't find any element of the web component.
  • If we define some style for, let's say, a <div class="button">, if inside the web component there was also a div with the same class, it would not be affected by the outside styles.

Regarding the code, a web component with Shadow DOM could look like:

<my-profile name="Marty" lastname="Mcfly">
  #shadow-root
  <div class="profile-picture">
    <img src="marty.png" alt="Marty Mcfly" />
  </div>
  <div class="name">Marty Mcfly</div>
</my-profile>

Furthermore, we have a special case worth talking about: <slot />, a.k.a. Light DOM. As previously stated, Slots are the way we have to customize the content of our web component. As they are not part of Shadow DOM, and therefore, are not encapsulated, they are affected by page styles and can be queried. There are some exceptions and details to take into account related to Shadow DOM that will be analyzed in detail later in future posts.

ES Modules

Finally, we have ES Modules API. With this API we can load JavaScript modules. This feature allows us to reuse JS files by importing/exporting them both from JavaScript code as well as from HTML code, using that case type="module":

  • From JS code:
import { formatter } from "DateFormatter";
  • From HTML code
<script type="module" src="my-web-component.js"></script>

This API is leading the way to new features that are currently being developed like HTML Modules, JSON Modules and other ways to import files from HTML and JavaScript.

Benefits

The APIs provide lots of benefits such as:

Encapsulation

As mentioned before, this is probably the most important feature and benefit of using web components. Encapsulation assures that our code will be isolated from any other element of any framework or feature already present in the page where the component belongs to, avoiding conflicts and not desired behaviours.

Reusability

Encapsulation and ES Modules bring us reusability. We can generate reusable components than can be used and imported easily on many sites and platforms. A common use case of this is the use of web components both for desktop and mobiles sites.

Flexibility

Web Components can be customized in many ways. For instance, we can customize behaviour using attributes/properties, content using slots and styles using CSS vars. This gives us lots of flexibility and a raw component could take lots of different shapes.

Performance

Web Components provide a standard specification for some features that previously were only available using third party libraries. That way we can dispense with external dependencies. This implies direct benefits as reducing the complexity and size of our code and bundles, and therefore, improving the loading page time.

Compatibility

Modern browsers (and therefore their JavaScript engines) try to be always up to date to the latest standard features defined by the official organizations like W3C and TC39. So using Web Components ensures greater compatibility with modern browsers.

The following table provides a summary:

For those features that are not available in older browsers, we can use polyfills, like the ones provided by WebComponents.org (Github repo)

It's important to note that Internet Explorer 11 does not support Shadow DOM, although it's still commonly used, especially in banking environments.

Useful links and resources

Here are some good resources about Web Components:

Originally written by Rafa Romero Dios for JavaScript Works

18