Web Components, Micro-Frontends and Domain Driven Development are just a few buzzwords that seem to be getting thrown around in the space of Web Development at the moment.

This is Part 1 of a series of articles that will outline the what/why/how. We'll explore what are Web Components and why we care, their use cases and how/where Angular Elements comes into play.

In part 2 we look at:

  • Building and Bundling Web Components in Angular.
  • Other approaches available to us using IVY.
  • Some limitations and possible solutions.
  • Some key tools.

Although many parts will be directed towards Angular, the great thing about Web Components is their framework agnostic nature.  The concepts herein described therefore, can/are applicable to other frameworks and even VanillaJS.

What are Web Components#

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

In other words Web Components gives the developer the ability to create custom elements that the browser will render and treat just like a builtin element, e.g <button>.

Why do we care?#

On the surface, it might seem like this is not a big deal.  Despite implementation differences, web frameworks generally have the idea of reusable code (components) that will render to the DOM.

Let’s look at a Date Picker.

Googling “Javascript Date Picker” you will see multiple results. Some will be written in vanilla Javascript, others using jQuery and then a plethora for the different frameworks.  The implementations will range vastly but most share a huge amount of the same functionality.

If you are a solo programmer or small company that only codes in one framework, this really is not a problem. You pick one and run with it.  For larger companies where they may use multiple frameworks, enforcing design and functionality consistency across the software estate can be challenging.  

Having one Web Component that can be reused across all frameworks and give consistent functionality can save a lot of work and headaches.

Other use cases#

Creating simple Web Components, like a Date Picker, is great when you want to build a reusable component library, but can we do more? A trend that is going around at the moment is Micro-Frontends.

The idea behind Micro-Frontends is you arrange your frontend application as a collection of loosely coupled smaller apps. These smaller apps aim to mirror the microservices in the backend.  This creates encapsulated domain level vertical slices e.g. Login, Customer Profile, Products, Order Process etc.

This vertical slicing gives domain/app autonomy and in turn leads to: incremental upgrades, independent deployments and a decoupled code base.  A great benefit when you start to think about Enterprise level applications/codebases.  Web Components are huge enablers of this architecture design approach.

If we think about a single Web Component containing multiple components that encapsulates a single domain we start to see a clean path for Micro-Frontends.

How to use Web Components?#

The great thing about Web Components is that they are treated by the browser as normal html elements.  They have a constructor and lifecycle callbacks. When the html is parsed, the browser instantiates a new node for our Custom Element.

Using the CustomElementRegistry object, we can add new Custom Elements to the page.

class HelloWorldClass extends HTMLElement {

    constructor() {
        // Always call super first in constructor
        super();
    }
    …
    …
}

customElements.define('hello-world', HelloWorldClass);

With the last line of code we are registering the HelloWorldClass, which will be used when the tag “hello-world” is parsed.

Simple right? Well yes it is if all you want to create is a helloworld type Web Component.  What about all those good things that we are used to, like custom events, attributes, properties and the reflection of properties to attributes? Things become a bit more involved:

class HelloWorldClass extends HTMLElement {
    static get observedAttributes() {
        return ['title’];
    }
    
    get disabled() {
        return this.hasAttribute('disabled');
    }
    
    set disabled(val) {
        if (val) {
            this.setAttribute('disabled', '');
        } else {
            this.removeAttribute('disabled');
        }
    }
    
    constructor() {
        // Always call super first in constructor
        super();
        // Setup a click listener on <hello-world> itself.
        this.addEventListener('click', e => {
            ...
        });
    }
    
    // Only called for the disabled and open attributes due to observedAttributes
    attributeChangedCallback(name, oldValue, newValue) {
        switch (name) {
            case 'title':
                this.component.title = newValue;
                break;
        }
    }
    
    get title(){
        return this.hasAttribute(title);
    }
    
    set title(value) {
        if (val) {
            this.setAttribute(‘title’, val);
        } else {
            this.removeAttribute(‘title’);
        }
    }
    
    …
}

As you can see things slowly get more complicated and this is only for one event and one attribute.

In the world of angular there is @angular/elements which is here to help. This package will take a component of your choice and wrap it in a Custom Element. It automatically creates all the bindings for you and scaffolds everything the component requires to manage the view and state.

The best part is you can do it in two lines:

import { createCustomElement } from '@angular/elements';

createCustomElement(component, config);

The Web Component spec has enabled developers to create reusable components that work natively with the browser.  This is great for creating a component library. It gives design and functionality consistency across your company’s app suite, but does not restrict your developers to any one framework.  Taking the Web Components to the next level, we can use it to help architect and implement Micro-Frontends.  The almost drag and drop functionality that we get means domain development teams can work autonomously. The concept of decoupling service and creating microservices has been a widely adopted backend architecture and now Web Components can help bring it to the frontend without having to consider less desirable web technologies like iFrames.