Web Components: When, Where, and Why to Use Them
In my opinion the best way to write them is by using Stencil in terms of speed/comfort of writing or even speed of the Components library.
This article tries to summarize all the knowledge I have about Web Components: when, where, and why to use them? What is Stencil and why to use it? How (by my opinion) should we write Web Components.
What are the Web Components?
I could do the copy paste work here, but I’m lazier than that, and the article would be even longer…
So here are the links to Mozilla Developer page.
When, where, and why to use them?
You could ask yourself a question: Why do I or my organization need a Web Components? We have X projects that are written only in *Pick one of* [React/Angular/Vue] (from now on I will call them Big 3) and we are fine with it. We can create a component library or use mono repo to share them. - yeah you are right if you have one technology there is no point of using Web Components, I completely agree with that, but that can change.
But let’s go in time to a few years back. Change the name of the library/framework you use currently to jQuery or AngularJS, now go back to current year and you are here with that old library your boss tells you to rewrite them to one of Big 3 with limited resources both time and money, and that situation could occur every let’s say X years because when your boss see something new and shiny he wants it.
Web components are based on existing web standards so you can use them now or 5 years in the future with your brand-new library, and make your organization/project more “Futureproof”.
We had an internal app made in Angular ( ver. 6 I think). After finishing it my boss came with an idea for another one. As developers, we thought: That is great we can create an NX (Narwal monorepo) and reuse some of our previous work. But after a week the decision made by CTO was: Let’s try React. Creating an Angular Component library was not an option, as we had to add Angular, and more, and more libraries with it making new app heavy just in the beginning.
After it, we created the Web components library, used it in the new React app, and switched in Angular one for design consistency, and for Future CTO decision changes.
Web components are not only “Futureproof”, but also as I call it “Which JS technology we will use change proof”, which means single source of design truth.
To be on the same page with everyone I’m not saying that we should drop our frameworks/libraries and start writing everything with the help of Web Components (well that is possible to be done). Use them as a helper which can boost the process of development, or at least skip a process of designing new UI components.
How to write to them?
When I and my team were writing Styled Component library, we came with a couple of decisions/style guides, which were helpful in creating them:
- Try to keep it as simple as possible - Treat them as components with styles or as a container. It should only know about self behavior, but there are exceptions which lead to the second rule:
- If they need to control, they need to control only other known Web Components:
The best example here is <form-controler/> which accepted <input-component/> and <button-component type="submit" />, this helped with handling the events for the whole Form, instead of a single input (without closing possibility to do it). - Instead of creating 12345th prop inside Component, create Base Component and then Specialized Component as a wrapper. Live example: Our PM wanted Phone Input with a lot of features (similar to this). Instead of Creating <input-component type="phone"> with a lot of if logic inside we created Specialized <phone-component/>, which was on top of <input-component/>.
- Use many CSS variables - You could ask: Wait, What? But you have mentioned that they were meant to keep the same UI in an organization. Yes, they are but also keep in mind that there could be a need to change for example in a Theme (day/night) mode. Adding variables does not mean that the whole design will change, but it leaves the option to make small changes.
Ok, so why Stencil? And WTF is that anyway?
Why not vanilla JS Components? I will answer it by another example (this time code)
Vanilla JS code:
const template = document.createElement('template');
template.innerHTML = `
<style>
button, p {
display: inline-block;
}
</style>
<button aria-label="decrement">-</button>
<p>0</p>
<button aria-label="increment">+</button>
`;
export class AppCounter extends HTMLElement {
static get observedAttributes() {
return ['value'];
}
set value(value) {
this._value = value;
this.valueElement.innerText = this._value;
this.dispatchEvent(new CustomEvent('valueChange', { detail: this._value }));
}
get value() {
return this._value;
}
constructor() {
super();
this._value = 0;
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(template.content.cloneNode(true));
this.valueElement = this.shadowRoot.querySelector('p');
this.incrementButton = this.shadowRoot.querySelectorAll('button')[1];
this.decrementButton = this.shadowRoot.querySelectorAll('button')[0];
this.incrementButton
.addEventListener('click', (e) => this.value++);
this.decrementButton
.addEventListener('click', (e) => this.value--);
}
attributeChangedCallback(attrName, oldValue, newValue) {
if (attrName === 'value') {
this.value = parseInt(newValue, 10);
}
}
}
customElements.define('app-counter', AppCounter);
Same Web Component in Stencil:
import { Component, Prop, Method, Event, h, EventEmitter } from "@stencil/core";
@Component({
tag: "app-counter",
styleUrl: "app-counter.css",
shadow: true,
})
export class AppHome {
@Prop({ mutable: true }) value: number = 0;
@Event() valueChanged: EventEmitter;
@Method()
async getValue() {
return this.value;
}
increment() {
this.value++;
this.valueChanged.emit(this.value);
}
decrement() {
this.value--;
this.valueChanged.emit(this.value);
}
render() {
return (
<div>
<button onClick={() => this.increment()}>-</button>
<p>{this.value}</p>
<button onClick={() => this.increment()}>+</button>
</div>
);
}
}
See? It is more readable, and it is only a simple counter.
To answer the second question I copied the description from its page:
The stencil is a compiler that generates Web Components (more specifically, Custom Elements) and builds high-performance web apps. Stencil combines the best concepts of the most popular frameworks into a simple build-time tool.
Stencil takes features such as:
- Virtual DOM
- Async rendering (inspired by React Fiber)
- Reactive data-binding
- TypeScript
- JSX
- Static Site Generation (SSG)
Source and more here
Why Stencil, and not other Web Components libraries/frameworks/compilers?
Out of box jsx template, Typescript, and test environment bought me right away (and that is my personal opinion, I’m open to discussions). Also when it comes to speed/comfort of writing or even speed of the Components library it looks great (Stencil.js vs lit-element vs Vanilla vs Shadow DOM vs Vue.js What is the best solution for Web component). What’s more, it is testable, and we can easily connect Storybook.
Web components - answer to all of our problems!
Actually, no. There is no such thing. Web components are also an example of it…
The end usage of it is almost the same as native elements, except React:
Because React implements its own synthetic event system, it cannot listen for DOM events coming from Custom Elements without the use of a workaround. Developers will need to reference their Custom Elements using a ref and manually attach event listeners with addEventListener. This makes working with Custom Elements cumbersome.
For more: https://custom-elements-everywhere.com/