Software Developer
They keep getting better every day
A while back I re-wrote my website. Unhappy with the performance hits of large frameworks, I ripped out Next.JS and React along with it. I replaced those with my own custom build system powered by Markdoc. You can read more about the journey and the performance gains here.
At the time, I made a decision to use an HTML renderer for my pages and to use Web Components to add progressive enhancement where interactivity was desired.
Web Components were a mixed bag of a choice. They run natively in the browsers, and so no framework is required to create custom elements and leverage the Shadow DOM. But their API is not ergonomic (i.e. they're super verbose to write). And they necessarily require a browser to run, which for a time ruled out server-side rendering (SSR).
But things are changing. Quickly. And I think this is really good news.
There are a number of non-framework... err... frameworks... emerging for creating Web Components, which improve the ergonomics of writing custom elements. I'll briefly mention my favourites.
Lit is probably the most popular and well known. You can, for example, start a new Vite project with Lit. It is also a default in the Open WC generators. Lit is cool because it allows you to write your Web Components in TypeScript, with JSX-like syntax. It uses tagged template literals, which means you don't need to learn custom syntax and can start writing custom elements without fuss. In addition, Lit has a tiny footprint, weighing in at 5kb (minified and compressed). A small overhead for a dramatically better authoring experience.
Here's an example of a web component written in Lit:
import {html, css, LitElement} from 'lit';
import {customElement, property} from 'lit/decorators.js';
@customElement('simple-greeting')
export class SimpleGreeting extends LitElement {
static styles = css`p { color: blue }`;
@property()
name = 'Somebody';
render() {
return html`<p>Hello, ${this.name}!</p>`;
}
}
Stencil is a more comprehensive library than Lit for building reusable, scalable design systems. On the surface, Stencil seems similar to Lit. You can use TypeScript. The syntax is JSX-like. There are decorators that shortcut a lot of things for you. But the true power of Stencil is, for me, in its tooling as a library for creating design systems. Out of the box, it gives you everything you need to write your components, test them, document them, and define their types.
Here's an example of a web component written in Stencil:
@Component({
tag: 'ds-text',
styleUrl: 'ds-text.css',
shadow: true,
})
export class Text {
render() {
return (
<slot name=”ds-corp-text”>
Your Text Rendered Here
</slot>
);
}
}
But wait, there's more!
Stencil doesn't just allow you to build and output Web Components for use in your projects; it also can compile your Web Component into framework-specific component code (such as Vue, React, Angular, etc.). This makes Stencil a great choice for teams looking to build a future proof design system at organizations that might be using Micro Frontends with different frameworks and tech stacks.1 You can write a component once, and compile it for Web (as a custom element), React, Vue, etc.
Modern web sites and applications rely on static-site generation (SSG) and server-side rendering (SSR) to dramatically improve performance outcomes over traditional client-side only rendered (CSR) applications.
Using Web Components in your architecture can create a lot of headache if you're doing SSR. Why? Because until recently, the only way to use the native Shadow DOM was to construct a shadowRoot
using JavaScript at runtime. There was no built-in way to express Shadow Roots in the server-generated HTML.
An important negative side effect of this lack of SSR support is layout shifting after the page has loaded, or temporary displays of a flash of unstyled content ("FOUC") while loading the Shadow Root's stylesheets. When a custom element is rendered by the server, but then has its shadowRoot
created at runtime to "hydrate" the component, this can be the result. And it requires clever hacks to engineer around.
Until now.
Declarative Shadow DOM (DSD) is a web platform feature currently in the standardization process that removes this limitation, bringing Shadow DOM to the server. And last week, the Chrome Dev team announced it would be enabled by default in Chrome 111.
You can read more about how DSD works at the links above. But the TLDR is, with the introduction of DSD, it's now possible for a Custom Element to have a shadowRoot
before it gets upgraded. This means the element will have a shadowRoot
property already available when it is instantiated, without your code having to explicitly create one.
As a new web platform API, Declarative Shadow DOM does not yet have widespread support across all browsers. But change is in the air. And the future for Web Components is looking bright.