The Case for Custom Elements: Part 1

In this post I want to make the case for why I think Custom Elements make sense, especially for large organizations. In Part 2 I will present a deeper dive on the features that make Custom Elements so compelling. I’ll also explore topics often overlooked when discussing Custom Elements, such as their ability to work with Virtual DOM and be rendered on the server.

Background: Pattern Libraries

Many large organizations often consolidate their front-end UI code into pattern libraries to help ensure brand consistency.

Here’s an excellent example from MailChimp:

Page showing styled buttons and the related HTML markup for MailChimp

Pattern libraries are extremely useful as a company grows and splits into multiple teams. Developers can re-use the components from the pattern library to achieve a consistent look without needing to recreate it from scratch each time.

But pattern libraries also present an interesting challenge. Because there might be many teams within an organization, each using their own favorite front-end frameworks (React, Ember, Angular, etc.), how do you build a pattern library that works for everyone?

  • Problem #1: You can’t choose any one framework to build your components because then you’d be locked into that stack and not all of your teams would be supported.
  • Problem #2: Writing all of your components in vanilla HTML/JS can be quite verbose.

To demonstrate Problem #2, here’s a Salesforce datepicker written in vanilla HTML. It’s a very polished component:

A datepicker showing days of the week for the month of June

But it ends up being 179 lines of HTML (here’s a snippet to show how repetitive this can get):

Several repeating lines of HTML

To solve both problems we can instead build our pattern libraries out of Custom Elements. Custom Elements give us a built-in component model without requiring framework lock-in.

Custom Elements in a nutshell

Custom Elements let you extend HTML and define your own tags. If you’re familiar with building components in Angular or React, creating a Custom Element should feel similar. The primary difference is that they’re native to the browser and based on a W3C spec. Because of this, they should be able to work in any context where HTML works. This includes being used by the aforementioned frameworks.

To define a Custom Element, start by writing a class that extends HTMLElement.

/* my-element.js */
class MyElement extends HTMLElement {
  // This gets called when the HTML parser sees your tag
constructor() {
super(); // always call super() first in the ctor.
this.msg = 'Hello, World!';
}
  // Called when your element is inserted in the DOM or
// immediately after the constructor if it’s already in the DOM
connectedCallback() {
this.innerHTML = `<p>${this.msg}</p>`;
}
}
// This registers your new tag and associates it with your class
window.customElements.define('my-element', MyElement);

Every Custom Element has a set of callbacks, known as “reactions”, which are triggered in response to lifecycle events. In the above example I’m using the connectedCallback to stamp out my element’s DOM. I’ll cover reactions in more detail in Part 2.

Including the my-element.js script in your page will register the element and any instances already in the DOM will be automatically upgraded.

<body>
<my-element></my-element>
<script src="my-element.js"></script>
</body>

Confusion surrounding Web Components

One issue I’ve noticed is that many developers have a hard time getting their heads around what Custom Elements offer as a stand-alone technology. This isn’t made any easier by the fact that the term “Web Components” is often used interchangeably with Custom Elements. In particular, developers often conflate Custom Elements and Shadow DOM, despite the two being totally separate.

Let me try to tease the two apart here.

Custom Elements:

  • Let you define your own HTML tag with bundled JS behavior
  • Trigger lifecycle callbacks
  • Automatically “upgrade” your tag when inserted in the document

Custom Elements don’t:

Although many examples combine Custom Elements with Shadow DOM in order to showcase the two technologies, there is no requirement to use the two together. While they are complementary, each spec was designed to work stand alone as well.

I would encourage anyone interested in Custom Elements to start by using them without Shadow DOM so they can better understand how the two technologies work in isolation and what each brings to the table.

It’s not a huge step to go from a jQuery or framework-based component to a Custom Element, and then layer in Shadow DOM later if necessary.

jQuery plugins/framework components to custom elements to custom elements with shadow dom

As you move from framework specific components to Custom Elements, you may find that all your needs are met. If you find you do need style scoping or re-projection, you can layer in Shadow DOM as necessary.

Browser Support

If you’ve followed Custom Elements, you may know that Chrome and Opera originally shipped an earlier version of the spec, known as Custom Elements v0 (that’s supposed to be a “v” and a zero but Medium’s typeface is stupid here).

This version is now considered deprecated as all browsers move toward implementing Custom Elements v1.

If you want to start working with Custom Elements v1 today you can!

To use Custom Elements v1 you can rely on the polyfill created by the Polymer team which is a mere 2.5kb minified, gzipped. The polyfill supports IE 11, Edge, Safari >6, Firefox, Chrome, Android Browser >4.4, and Opera. Because IE10 lacks support for Mutation Observers there is an additional shim you will need to include if you need to support that browser.

There’s also a polyfill created by Andrea Giammarchi which is 5kb minified, gzipped and has both v0 and v1 support. Andrea’s polyfill claims to have experimental support down to IE8, you can see the full list of tested browsers/devices here. This is also the polyfill used by the AMP project.

Wrapping up

If you’re an organization considering building a pattern library, Custom Elements just make sense. They’re part of the platform, so they can be used by any stack. They don’t require additional frameworks or libraries. And you can write them in pure ES2015 using classes and modules and all the other goodies that JavaScript has to offer. If you’re interested in other technologies like Virtual DOM, TypeScript, or server-side rendering, check out Part 2 where I demonstrate how to leverage these alongside Custom Elements. And if you haven’t had a chance be sure to check out Daniel Buchner’s excellent post on dispelling Web Components myths.

Till next time!