Javascript Components

My journey towards Web Components

Albert Skibinski
My journey towards Web Components

I was exploring current front-end frameworks and libraries to get a better understanding where we stand and how we got here. I was especially interested in the current capabilities of native web components (in vanilla JS) using web standards and how they compare to these frameworks and libraries. I decided to write up all my notes into this post, maybe they prove useful for somebody. 

First, some terminology.

Let me know what you think!

Imperative versus declarative

These are general programming paradigms but in the context of front-end development, a declarative representation of UI has become a standard approach for front-end web development. This offers a way of writing templates/views with data binding and separating the control flow. It's also described as creating UI as a function of state.

Imperative on the other hand is the sequential way of taking steps to complete a task. We all know imperative using basic vanilla JS or jQuery for DOM manipulation. When you are talking about views, you are talking about templates and their engines. And there are several flavours.

String-based template engines

These are template engines like mustache which uses innerHTML and are the least efficient but they used to be popular because DOM manipulation was slow in older browsers. And innerHTML also has security concerns. But the concept is DOM independent, which could be seen as a pro, especially if we think about server-side rendering (SSR). By the way: Twig is also a string template engine, but in PHP.

DOM-based template engines

Examples which use these are Angular, Alpine, and Vue (Vue only when using DOM templates). In all cases, the templates are parsed which can cause issues. For instance, (Vue developers warn about DOM templates) because they can be unpredictable: the browser renders the markup into a DOM template which is then used by the framework and it might disagree on some things. It's also impossible to do SSR since we have no browser server-side.

JSX

JSX is not a template or engine. It's declarative syntax that’s used to express the virtual DOM after it is parsed by babel into JavaScript objects. While JSX may look like HTML templates, it's not. It's not even React-specific (Stencil uses it too) and is used to create components that share markup and logic. This is a better fit for component-based architecture which React started.

Virtual DOM

Not a template but a technique to effectively update the DOM. The best explanation I found on the virtual DOM is actually a Svelte blog post which explains how React uses this to diff the DOM against the VDOM and make the necessary changes.

Architecture

All three major frameworks (Angular, React, Vue) have converged to the component model and an MVVM (Model-View-ViewModel) architecture.

  • Model (M): The object which contains data and business logic.
  • ViewModel (VM): Component code data binding into the view (and potentially back) and managing simple state (presentation logic).
  • View (V): How the visuals look in the browser. The DOM + styles.

Note: Depending on the implementation, you might only have a VVM architecture, for example, if you have a simple React site which pulls data directly into the component, without using a data layer like GraphQL.

Components, Custom Elements, and Web Components

The three major frameworks use Components but none of them use web components by default. What do we mean by native web components (or just web components)? Native web components use three main technologies. Although I've seen ES module specification being mentioned as a fourth.

  • Custom Element
  • Shadow DOM
  • HTML templates and slots

You can just create Custom Elements without shadow DOM and template/slots; it's optional. In fact, many developers just use Custom Elements because the Shadow DOM has challenges of its own (specifically with styling and SSR, more on that below).

lit-html + lit-element

The smart people behind Polymer have created two libraries: lit-html which makes creating declarative templates – UI as function of data – easier (like the frameworks do) but with minimal overhead. And lit-element which essentially is a base class making it easier to create Web Components. Lit-html feels like JSX but internally it leverages tagged template literals to very efficiently update the DOM. Lit element creates Custom Elements with shadow DOM by default. So it seems like a nice way to create web standard components with data binding, minimal overhead, and a lot of flexibility. In this repo are two simple examples of a lit-html and lit-element.

Stencil

Another tool is Stencil from the smart people of Ionic. It's not really a framework nor a library but a compiler which creates web standard web components or entire PWAs. It uses JSX and TypeScript and compiles to native JavaScript. This makes it perfect for integration with other frameworks or tools like Storybook, although you can also use Stencil as a component library on its own. Here is my Stencil playground repo.

Svelte

Svelte isn't really a tool to create Web Components but rather an alternative with a different approach compared to the existing frameworks. I still think it's worth mentioning because it does have some similarities with Stencil as they are both compilers and compile into native JavaScript components which can be used by third parties (or standalone). But Svelte explicitly does not use a virtual DOM. It instead compiles to imperative code that updates the DOM, which may be faster. By default, Svelte does not create Custom Elements but it is possible with an option. Svelte has TypeScript and better support for testing on the roadmap.

What problems are we solving anyway?

So why might we prefer native web components over components provided by the frameworks? Interoperability and reusability using standards. Stencil clearly aims for being the tool to create web components which can be used in various frameworks without any dependencies.

Other issues

SSR and Shadow DOM

 You can't declaratively create web components with shadow DOM without JavaScript: it doesn't exist until we customElements.define().

Accessibility

Web components should be built with accessibility in mind, just like any other HTML.

Phew

After having spent (probably too much time) exploring Web Components, various frameworks, tools and libraries and upcoming proposals, I feel web components have come a long way since I first played with them in Polymer 1.0 back in 2015.

Albert Skibinski

About the author

  • Albert Skibinski is a freelance full-stack developer en co-founder at Jafix.
  • I write about web development, long bike rides and food!
Back to overview