BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News Web Components at GitHub - Web Component SF Meetup

Web Components at GitHub - Web Component SF Meetup

This item in japanese

Kristján Oddsson detailed at the Web Components SF meetup how GitHub uses Web Components and the patterns GitHub identified to foster readable, performant, and accessible front end components.

Oddsson started by illustrating how a simple behavior would be implemented in vanilla JavaScript at GitHub:

on('click', 'js-hello-world-button', function (event) {
  const button = event.currentTarget;
  const container = button.closest('js-hello-world');
 
  const input = container.querySelector('js-hello-world-input');
  const output = container.querySelector('js-hello-world-output');

  output.textContent = `Hello, ${input.value}`
}

with the corresponding HTML on the page:

<div class="js-hello-world">
  <input class="js-hello-world-input" type="text">
  <button class="js-hello-world-button">
    Greet
  </button>
  
  <span class="js-hello-world-output"></span>
</div>

The previous code does not rely on any abstractions beyond the standard web APIs and as such may be among the most efficient way to write the required behavior (displaying a welcome message on a button click). However, Oddsson mentioned a few issues with the approach: a naming scheme is required, the DOM querying is explicit, classes are not scoped and could be overloaded unwillingly. This results in more work on the developer side.

Oddsson then explained that the team at GitHub has been converting behaviors like the previous one into progressively-enhanced custom elements. GitHub’s custom elements align as much as possible on built-in elements behavior. GitHub custom elements additionally do not use the shadow DOM.

Oddsson then uses the <auto-check> custom element — that automatically check the availability of a repository name while the user is typing, to illustrate the boilerplate involved in writing web components.

First, custom elements define an interface that includes four methods (connectedCallback, disconnectedCallback adoptedCallback, attributeChangedCallback) corresponding to different stages of the lifecycle of the element. An observedAttributes method also specifies which attributes to notice change for. Additionally, a custom element typically would have methods to retrieve and set the attributes that constitute the interface it exposes to the component users.

Then custom elements must be added to the registry if they are not already there (window.customElements.define method). The DOM elements that are involved in the behavior must also be queried — this.querySelector('input') for instance retrieves the input child element where the user enters the repository name. Additionally, as GitHub uses TypeScript, the relevant types must be created:

declare global {
  interface Window {
    AutoCheckElement: typeof AutoCheckElement
  }
  interface HTMLElementTagNameMap {
    'auto-check': AutoCheckElement
  }
}

Oddsson explained that in exchange for the boilerplate involved in writing web components, they solved two of the previous issues with the implementation of behaviors in vanilla JavaScript: a naming scheme is no longer needed, and custom elements simplify scoping.

Oddsson then introduced Catalyst, a collection of technologies that solves some more issues. Catalyst involves custom elements, TypeScript and helper decorators. With Catalyst, the first example (welcome message behavior) would be implemented with the following HTML:

<hello-world>
  <input data-target="hello-world.input" type="text">
  <button data-action="click:hello-world#greet">
    Greet
  </button>
  
  <span data-target="hello-world.output"></span>
<hello-world>

and code:

import {controller, target} from "@github/catalyst"

@controller
class HelloWorldElement extends HTMLElement {
  @target input : HTMLInputElement
  @target output: HTMLElement

  greet() {
    this.output.textContent = `Hello, ${this.input.value}`
  }
}

The boilerplate is as a result of Catalyst significantly reduced vs. the original version. As Oddsson mentioned, the technique takes inspiration from the stimulus framework. The Stencil web component compiler also uses decorators to limit boilerplate and produce optimized code. LitElement, and Microsoft’s FastElement also uses decorators to annotate and simplify code.

Because GitHub is using direct DOM manipulation instead of an abstraction on top of the DOM (e.g. virtual DOM or rendering library), GitHub components are kept small and performant. The auto-check component weighs 1.3KB with one 400 bytes dependency. GitHub custom elements are all below 3KB. One of the few components above 2KB is the auto-complete input element at 2.5KB. This contrasts with other custom elements distribution strategies that package peripheral content (e.g. icons, CSS) and abstractions (e.g. DOM diff library) together with core functionality. Developers may thus find custom button elements that weigh 13KB, or custom input elements that weigh 20KB.

GitHub’s 17 custom elements are open source and available online. The full talk is available in YouTube and contains more code examples, technical details and a Q&A session with attendees.

Rate this Article

Adoption
Style

BT