Web Components with Stencil.js — is it the best way to create reusable UI elements in 2018?

Kuba Holak
MUG.
Published in
6 min readMar 27, 2018

The ability to develop universal, framework and library agnostic UI elements that can be shared among different projects and teams seems to be the perfect medicine for at least part of what we call a framework churn in modern front end world. Ever since I’ve started working as a web developer I witnessed a number of elements and interactions that can be abstracted away to a reusable code, but differences between frameworks and their implementation details were making it cumbersome… Web components seem to be an answer.

But wait, what are the web components?

Web Components is a suite of different technologies allowing you to create reusable custom user interface components — with their functionality encapsulated away from the rest of your code — and utilize them in your web apps.

https://developer.mozilla.org/en-US/docs/Web/Web_Components

Millions of people around the world already use this technology every day. Last year’s second most popular website, youtube.com, is based on Web Components. Even History tab in Chrome is written using Polymer, a Web Components library! As per Wikipedia, Web Components were introduced for the first time by Alex Russell at Fronteers Conference 2011 for the first time.

A few words about technologies involved

Shadow DOM

Encapsulation is probably the key to success when thinking about creating reusable elements — being able to separate markup, styling and implementation details from the rest of the document is what makes the idea of Web Components so attractive. The Shadow DOM API does the trick. It’s browser’s API, is what allows you to create the scoped subtree within DOM element.

You can affect the nodes in the shadow DOM in exactly the same way as non-shadow nodes — for example appending children or setting attributes, styling individual nodes using element.style.foo, or adding style to the entire shadow DOM tree inside a <style>element. The difference is that none of the code inside a shadow DOM will affect anything outside it, allowing for handy encapsulation.

https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM

Shadow DOM has two modes: open and closed. The closed mode of Shadow DOM provides component authors with control over how the Shadow Root of their component is exposed via js. You can read more about shadow DOM here.

Styling

Styles in Shadow DOM are scoped and don’t leak out. Global CSS will affect Shadow DOM elements just as any other html element — by inheritance of properties, so setting font-family on body will be inherited by the custom scoped component. Setting styles to * will also affect the Shadow DOM rooted element, as it affects all elements. If you want to allow your component users to style the component, you should use custom CSS properties.

Custom Elements

With custom elements we can create custom html tags, or enhance existing tags and components. Thanks to this technology, web components will look just like regular html tags, for example <my-custom-button my-custom-attribute=”Some title” />.

Libraries to work with web components

Creating Web Components with vanilla js is of course possible (we need to remember about polyfilling for browsers with no support for required technologies), but writing components with plain js can be hard and require lots of boilerplate code. This is where web components libraries come in handy. You don’t need them, but trying to create production ready Web Components will be less painful using one of them.

Polymer

This is the most popular Web Components library, created and heavily used by Google. It provides simple API for creating components. Version 3.0, which will be shipped this year will use lit-html, that seems to be nice way to create html in js.

Skate.js

Skate.js claims to be a functional abstraction over the Web Components standards. The interesting part is that it allows you to use multiple view libraries, including lit-html, preact and even react.

Stencil.js

Stencil.js is relatively new Web Components compiler created by Ionic team. It takes popular modern web development concepts (such as Virtual DOM, async rendering, reactive event flow and TSX) and creates clean, standard-based web component code.

Creating Web Components with Stencil.js

I would like to tell you a little bit more about creating Web Components with Stencil.js. Why did I choose this library? Mainly because of built-in Typescript support and usage of reactive patterns that I am familiar with from my Angular and React experience, and of course stencil-is-not-a-framework approach. Stencil.js claims to be just a Web Components compiler, and created components are vanilla js — tiny and fast, additionally they can be imported using <script> tag, as well as ES6 imports. Small boilerplate lets you create components that are have only few kb of shipped code, and all the polyfills are served separately, based on browsers capabilities, so for standard compliant browsers shipped js code is only few kb per component.

Here is an example of very basic component:

Implementation details chosen by library authors make the component source code super readable and should be easy to understand if you have React (JSX!) or Angular2+ experience.

Most important decorators in Stencil.js are:

  • @Prop decorator lets you create parameters with access to component attributes. To validate attribute’s value you can use @Watch
  • @Event allows you to create Event Emitter and emit Custom DOM Events — for example to communicate with other DOM elements
  • @Listen allows you to listen to DOM Events
  • @State should be used to manage internal data. By changing values of state parameters you can trigger component rerender. You need to be aware of how change detection works in Stencil.js. For non-primitive values like objects and arrays change detection watches out only for assignment change, and not for the changes within the referenced object, therefore you should consider treating state parameters as immutable.

Stencil.js components are updated and re-rendered whenever state or prop changes. Components have their own lifecycle events, and they look very similar to React components lifecycle. You can use:

  • ComponentWillLoad
  • ComponentDidLoad
  • ComponentDidUpdate
  • CompontentDidUnload

Publishing web components created with Stencil.js

Stencil.js allows you to share single component or collection of components, which are super useful — if you created UI/UX elements for example like <my-custom-button> <my-custom-button-group> and <my-custom-nav>, there is a big chance that they will be used together. With Stencil you can create single component, but you can bundle them in collections as well, then distribute them using npm registry or even publish regular js files, and then import them using <script> tag. Once you export your collection or single component, you can use them on any website and web app, just like any regular component.

Why do I consider Stencil.js the best way to create reusable elements nowadays?

  • First of all — it uses modern web standards instead of abstractions. Want to fetch data? Use fetch. Want to access DOM element? Just access it via document object.
  • Second — implementation details and technologies behind the scenes made the development process very similar to coding apps with modern frameworks. Switching from creating React/Angular components to Stencil.js is easy. Usage of props/events for communication with outside world and state to manage internal data is very intuitive. Every developer in your team will be ready to work on components made with Stencil.js after 1–2h of reading documentation.
  • Third — jsx. I just prefer the idea of HTML in JS instead of JS in HTML.
  • Last but not least — speed and size. The idea of providing polyfills only when needed and relaying on native web technologies makes Stencil.js very attractive, as the boilerplate around each component is reduced to a minimum and the amount of kb sent each time is very small. Moreover, you just don’t care about polyfilling non compliant browsers — Stencil.js does it for you.

Summary

Of course — Stencil.js is relatively new and betting on such technology in production code is somehow risky, so if you are considering creating web components for production app and/or long term usage, you may consider using Polymer as it is more mature (and backed up by Google). But I encourage you to start experimenting with Stencil.js — hopefully it will grow and become an established technology, as it is heavily influenced by modern front end best practices and technologies(functional programming, immutability, scoped styling) and works using platform standards, what I believe makes it future proof.

--

--

Kuba Holak
MUG.
Writer for

lead R&D js geek @ Nordcloud. AWS certified.