What’s going on with Web Components anyways?

Like so many things do it started with a question. For us it was, “who’s using this polyfill anyways?” We were taking a hard look at our library and the Web Components polyfill that we were shipping; we knew that not all browsers supported web components but we realized that we hadn’t followed the progress of web components as they matured from specification to implementation. Since web components were introduced, where had they gone, who supported them, and what did the future look like for web components?

When I started at my job I had never heard of web components before, despite their being in slow development for nearly 5 years. Web components are a close cousin of other front end frameworks, especially AngularJS which it exanded upon and helped influence. At its inception circa 2010, AngularJS helped formalize the idea of declarative UI elements built with smaller HTML templates. Web components were imagined shortly thereafter to build upon the ideas of AngularJS and incorporate them into browsers as a web standard. Unfortunately, it is far more difficult to get browser standards confirmed by major vendors than publish a library, especially when web components contain multiple standards proposals.

It’s crucial to understand that the term web components actually refers to a collection of technologies instead of a single Web API. This can cause a lot of confusion since the webcomponentsjs package on NPM contains polyfills for each of the individual standards in web components. Web components consist of custom HTML elements, shadow DOM, module imports, and HTML templates. Browsers implent each of these technologies individually, each one with different amounts of coverage. Custom elements are the most outwardly recognizable part of the web components. They offer a browser native way to define reusable components just like in other JavaScript framework. Originally, custom elements were defined like this:

var SampleWebComponent = Object.create(HTMLElement)
document.registerElement('sample-webcomponent', SampleWebComponent)

While different browser vendors agreed that custom elements were a necessary idea, they differed on the implementation. Google, who was spearheading the efforts pushed ahead and was the only browser to implement the custom element v0 spec. After several years and multiple proposals custom element API evolved to:

class SampleWebComponent extends HTMLElement {
    constructor() {
customElement.define('sample-webcomponent', SampleWebComponent)

Version 1 of the spec is currently implemented in Chrome, and Safari and now in FireFox 63. Microsoft is currently considering implementation in Edge while Internet Explorer will not natively support the spec; the custom elements polyfill supports each browser.

The ShadowDOM specification was intended to encapsulate markup that goes with building web components. By attaching a shadow root to the DOM a developer can add a web component with a single HTML tag, while the component could contain as many tags as needed to build structure of the web component inside its own ShadowDOM. In addition to hiding the complexity of custom elements by providing an isolated DOM context, this allows localized styling to be applied only to web component.

There are some bits of shadow DOM terminology to be aware of:

  • Shadow host: The regular DOM node that the shadow DOM is attached to.
  • Shadow tree: The DOM tree inside the shadow DOM.
  • Shadow boundary: the place where the shadow DOM ends, and the regular DOM begins.
  • Shadow root: The root node of the shadow tree.

definitions courtesy of MDN

While everyone recognized the advantages of the ShadowDOM spec there were still a few contentious points growing out of how isolated the ShadowDOM should be from the DOM tree between versions. Originally in the v0 spec ShadowDOM’s were “open” meaning that the shadow root could be inspected and traversed by a developer looking at the DOM nodes inside shadow root. While Google believed that “closed” shadow roots could potentially interfere with tooling, testing, and accessiblity; since Javascript and the DOM were always intended to be open shadowDOM should be as well. Apple on the other hand argued that there should exist a use case for completely isolated shadowDOM that would not allow a developer to peer into the web component at all. Closed ShadowDOM could be used in ads, videos or to provide other proprietary widgets as a part of the isolated components proposal. Eventually the ShadowDOM v1 spec decided to allow both options thru the setting of the “mode” property. At least for the v1 version of ShadowDOM the isolation would be defined by the developer.

The ShadowDOM proposal followed a similar implementation track to custom elements. The initial proposal, Shadow DOM v0, was adopted by Chrome and Safari with FireFox keeping the implementation behind a developer flag. After being shipped early in Chrome it took several years to formalize the API which resulted in minor changes in ShadowDOM v1. Ultimately however ShadowDOM v1 was formalized and adopted in Chrome, Safari and FireFox. These changes have been referred back to the Web Platform working group of the W3C and ShadowDOM has been accpeted as a part the DOM standard.

Of all the parts of web components ShadowDOM changed the most between spec v0 and spec v1; take a closer look at the differences between the two as outlined by one of the specs authors, Hayato Ito.

Browser imports were another more divisive part of web components that Google charged ahead with. Originally web components spec called for something called HTML imports which would import all of the assets from one html page into another. Other browsers, namely FireFox, considered implementation but generally opted to wait and see how JavaScript modules would be implemented instead. Today all major browsers, including Internet Explorer 11, ship with native JavaScript module imports. While webcomponents.org still lists HTML imports as a part of the web components spec, the reality is they have fallen into disuse in favor of using browser imports. Older versions (the 1.x.x and 0.x.x release branches) of the webcomponents node module ship with an HTML imports polyfill. Currently Chrome is working on deprecating styling in HTML imports and is considered the first step in ultimately deprecating HTML imports all together.

Lastly, web components include HTML templates. They allow you to define a document fragment that can be reused elsewhere. When the document is loaded templates are checked for validity but not rendered until they are called in JavaScript. Unlike the other parts of the web components proposal, templates were not controversial and exist relatively unchanged since web components were proposed; unlike custom elements and shadowDOM there was only ever one specification for templates. As a result HTML templates are available in all four major browsers except for Internet Explorer.

Closely linked to but distinct from web components has always been Google’s Polymer library. While Chrome implemented the early v0 spec, adoption was slow and in 2013 they introduced Polymer, a library written around implementing web components. Looking at the versions of Polymer you can almost trace the advancement of web components. Polymer 1.0.0 features the distinctive “is” of custom elements v0 spec; Polymer 2.0.0 uses the v1 custom elements API (customElements.define) and ShadowDOM v1; version 3.0.0 deprecates HTML imports. For several years now the Polymer team spearheading web component adoption for several years and is one of the main drivers of their adoption.

With so many different JavaScript frameworks out there you would very easily be forgiven if you suffer from framework fatigue. In fact between the time you’ve started reading this and the time you finish it’s likely that two more frameworks will have been started and Angular will have another major release. Quietly though modern JavaScript web development has come quite a long way as the Big 4 browsers ship with more native features with every release. Despite a slightly twisted history and a long time maturing web components have become a broadly implemented standard for building browser native framework agnostic web applications ready for use.

Further Reading: