< Back
calendar

Jul 21, 2022

Everything You Always Wanted To Know About Web Components (But Were Afraid To Ask)

Here’s what you need to know

Header for posting Everything You Always Wanted To Know About Web Components (But Were Afraid To Ask)

How well do you know Web Components? What would you like to know about Web Components?

Here are the answers to the most common questions I get asked frequently.

Can Web Components work without JavaScript?

Yes, Web Components can be rendered without JavaScript through Declarative Shadow DOM.

Normally, a Shadow Root is attached to Web Component (technically a Custom Element) through JavaScript:

const shadowRoot = this.attachShadow({mode: 'open'});

this.shadowRoot.innerHTML = `<p>Hi there!</p>`;

Without JavaScript you can’t create a Shadow Root this way.

But Declarative Shadow DOM enables you to define a Shadow Root declaratively straight in HTML:

<host-element>
<template shadowroot="open">
<slot></slot>
</template>
<h2>Light content</h2>
</host-element>

When the HTML parser encounters this HTML, it will immediately apply the <template> element with the attribute shadowroot="open" as the Shadow Root of <host-element>, resulting in the following HTML, without any JavaScript needed:

<host-element>
#shadow-root (open)
<slot>

<h2>Light content</h2>
</slot>
</host-element>

Since no JavaScript is needed for this, Declarative Shadow DOM can be used to server-side render Web Components.

Now, don’t be afraid that you will need to write all your Web Components with this syntax from now on. That would kind of defeat the purpose of the encapsulation Shadow DOM brings.

When server-side rendering an HTML page, you can simply get the Declarative Shadow root for your element with the new getInnerHTML method:

const el = document.querySelector('host-element');
const html = el.getInnerHTML({includeShadowRoots: true}); // html is now:
`<host-element>
<template shadowroot="open"><slot></slot></template>
<h2>Light content</h2>
</host-element>`

getInnerHTML works just like innerHTML but you can pass {includeShadowRoots: true} as an argument which will return the element’s Shadow DOM as well.

At the time of writing, you can omit this argument in Chrome and it will return the Shadow Root by default.

Inside the server-side script you can simply replace the contents of <host-element> with this html and your element’s Shadow Root will now be rendered without JavaScript.

Keep in mind that Declarative Shadow DOM only takes care of rendering your Web Component without JavaScript.

For interaction like clicking etc. you will still need JavaScript but that applies to any component.

At the time of writing, Declarative Shadow DOM is only supported in Chrome, Edge and Opera.

Does React support Web Components?

You can use Web Components inside any JavaScript framework and React also supports this, but you will probably need to write a wrapper for your Web Component.

Out of all JS frameworks, React is not the easiest to integrate Web Components into.

Why would you choose Web Components over frameworks?

Like I wrote, you can use Web Components with any framework so you don’t have to choose.

But the benefits of Web Components are that they are standard, can be used anywhere (they’re just HTML elements) and provide truly scoped CSS.

What is scoped CSS?

The CSS you define inside your Web Component applies to your component only.

It will not affect any elements outside your Web Component, nor will the CSS for the outside document apply to your component, except for some standard styles.

This means Web Components provide truly scoped CSS without any needed naming conventions.

How do Web Components work with accessibility?

The beauty of Web Components is that they are just regular HTML elements, just like native elements as <p>, <div>, <h1> etc.

This means that making Web Components accessible is no different from making other HTML elements accessible.

You can add aria labels, keyboard and focus handlers etc.

Can I use external CSS?

Yes, you can.

While there are better ways to style Web Components, you can just load a stylesheet into a Web Component using <link rel="stylesheet">:

class HostElement extends HTMLElement {

constructor() {
super();

const shadowRoot = this.attachShadow({mode: 'open'});

shadowRoot.innerHTML = `
<link rel="stylesheet" href="/path/to/external.css"> <slot></slot>
`;
}}

Another, even more interesting option is to use constuctable stylesheets which make it possible to define and prepare shared CSS styles, and then apply those styles to multiple Shadow Roots.

This enables you to put shared CSS in a file like this:

// shared-css.js

const css = `h1 {
color: red;
}`

const sheet = new CSSStyleSheet();
sheet.replaceSync(css);

export {
sheet
}

which you can then use in multiple Web Components like this:

import {sheet} from './shared-css.js';

class HostElement extends HTMLElement { constructor() {
super();

const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.adoptedStyleSheets = [sheet];

shadowRoot.innerHTML = `
<h1>Hi there!</h1>
`;
}
}

Can I use SASS with Web Components?

You probably can, but you may only need variables since Shadow DOM can simplify your CSS dramatically and variables are already available through CSS Custom Properties.

So while you can use SASS, you probably won’t need it.

Do I need a third-party library to use Web Components?

No, you don’t.

Web Components are supported in all browsers now. Some experimental features may require a polyfill but the core features are now available everywhere.

Remember, Web Components are just like regular HTML elements and will be treated by browsers as such.

Can Web Components be server-side rendered?

Yes, they can with Declarative Shadow DOM. See the first question.

What are the most important pros?

Web Components are just standard HTML elements that can be used anywhere and can be integrated into any framework.

You don’t need any third-party code to render and use Web Components. Since they are just regular HTML elements browsers already know how to efficiently handle them.

While frameworks come and go, Web Components are a standard that is unlikely to ever be deprecated.

Another very important pro is that you can fully encapsulate the HTML and CSS of your component so you have truly scoped CSS.

This enables you to drastically simplify the CSS of your component since it is truly scoped, no convention needed.

What are the most important cons?

Web Components don’t provide features out of the box like data binding and declarative templates that frameworks do (although it’s not hard to implement these).

OMG, Web Components are not reactive???

Web Components don’t provide features like reactivity and declarative templates out of the box like most frameworks do.

But it’s not hard to implement these:

You could use something as simple as a setter to implement data binding.

Here’s a deliberately simplified example:

class HostElement extends HTMLElement {
constructor() {
super();

const shadowRoot = this.attachShadow({mode: 'open'});

this._name = 'John';

shadowRoot.innerHTML = `
<h1>
Hi <span data-bind="name">${this._name}</span>
</h1>
`;
}

set name(value) {
this._name = value;
this.shadowRoot
.querySelectorAll('[data-bind="name"]')
.forEach((element) => element.textContent = value);
}

get name() {
return this._name;
}
}

Here, elements that are bound to a property have a data-bind attribute whose content is the name of the property.

Inside the setter, all corresponding elements are looked up and their textContent property is set to the new value.

Of course, this can quickly become messy if you have a lot of bound properties so in those cases it’s probably better to use a tool like lit or StencilJS, but I hope you get the idea.

Can I use 11ty to compile Web Components to HTML?

Web Components are HTML so there is nothing to compile to HTML.

It is already HTML.

Web Components are just custom HTML elements that can be used in any HTML page.

Is it always smart to use Shadow DOM?

It really depends on your use case, but usually Shadow DOM will be a good fit since by definition a Web Component is a self-contained unit that will benefit from the encapsulation of HTML and CSS.

But if you don’t need it you can always create a Web Component that doesn’t use Shadow DOM.

What’s your question?

I hope this article has answered some of your questions on Web Components.

If you have any other question, please leave a comment here or send me a message on Twitter (@dannymoerkerke), where you can follow me for more writings on Web Components and the modern web.


Join Modern Web Weekly, my weekly update on the modern web platform, web components, and Progressive Web Apps delivered straight to your inbox.