Theodo logo

Can I Use Web Components

April 10, 2020Josselin d'Espinay14 min read

The goal of this article is to show you what is a web component, why you can now use it in production, and when you should do so.

For all those who already have some knowledge in web components, I would like to specify that I will be talking about the web component V1 standards as the v0 draft APIs are now removed from Chrome since Chrome 80.

If you’re new to web components, don't worry you can catch up starting from the v1 standards, looking at the v0 specifications and standards might not be useful unless you want to understand the journey of the web components to reach its V1.

But first, you might want to be able to understand the basic concepts of a web component.

Definition of a web component

So first and foremost, what is a web component?

"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

Simply put, the web component is not just a thing by itself. For a component to be called so, different technologies have to be combined.

What are those "different technologies" then?

I will go through the different technologies that make a web component. This article won't be able to encompass all the possibilities of a web component and all its possible implementations. For those who would like to go further into the explanations, I will provide some resources as you read through.

Custom elements

A web component is nothing more than an enhanced custom element. A custom element is a javascript class expanding the HTMLElement class that allows you to create a new HTML tag for your component.

First, you have to create a class as shown in the script below.

class MyComponent extends HTMLElement {
  constructor() {
      super();
      ...
    }
}

We then define a custom element with the previously created class.

customElements.define("my-component", MyComponent);

Your custom element is ready to be used as a tag in any web project.

<my-component></my-component>

let's try it in a simple html document:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>my first custom element</title>
  </head>
  <body>
    <my-component></my-component>
    <script>
      class MyComponent extends HTMLElement {
        constructor() {
          super();
        }
      }
      customElements.define("my-component", MyComponent);
    </script>
  </body>
</html>

And that's it! you have created a custom element. Now if you inspect your HTML document on a web browser you should be able to see something like this:

inspect my first web component

The main idea of custom elements is that you can create, extend, and reuse them everywhere in your code.

If you are looking for a more in-depth documentation, check the official documentation. I would also recommend this article on customElements that is giving you a good overview of the matter.

Shadow DOM

The next technology that we will be discussing is the Shadow DOM. When you attach a shadow DOM to your custom element you then scope all the CSS, and the HTML in this DOM. The big idea of the shadow DOM is to be able to create templates for your web components that won't be affected by any style or js selectors and therefore keep your code and feature as intended.

Let's try it in our previously created component :

First create a shadow root:

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

If you want to know more about the attachShadow mode argument I strongly recommend this article from Leon Revill.

Create some elements:

const spanElement = document.createElement("span");
spanElement.textContent = "Hello world";

Create a style element:

const style = document.createElement("style");

Create a css class for your span element and add some css rules:

style.textContent = ".my-class {color: red}";
spanElement.setAttribute("class", "my-class");

And finally attach them to your shadow root:

shadowRoot.appendChild(style);
shadowRoot.appendChild(spanElement);

so here it what your component should look like now:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>my first custom element</title>
  </head>
  <body>
    <my-component></my-component>
    <script>
      class MyComponent extends HTMLElement {
        constructor() {
          super();
          const shadowRoot = this.attachShadow({ mode: "open" });

          const spanElement = document.createElement("span");
          spanElement.textContent = "Hello world";
          spanElement.setAttribute("class", "my-class");

          const style = document.createElement("style");
          style.textContent = ".my-class {color: red;}";

          shadowRoot.appendChild(style);
          shadowRoot.appendChild(spanElement);
        }
      }
      customElements.define("my-component", MyComponent);
    </script>
  </body>
</html>

Now you can try going to the console and access and modify the elements in the shadow DOM but you won't be able to.

inspect my first web component with shadow root

As well if you add some style in your HTML file, it won't affect the shadow DOM elements. Try adding style on the span element for example:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>my first custom element</title>
    <style>
      span {
        color: blue;
      }
    </style>
  </head>
  <body>
    <span>Hey! I am just here for the example tho...</span>
    <my-component></my-component>
    <script>
      {...}
    </script>
  </body>
</html>

try to apply style on shadow DOM

As you can see the span inside the shadow DOM is not affected by the style.

HTML Template

"The template element is used to declare fragments of HTML that can be cloned and inserted in the document by script. In a rendering, the template element represents nothing. The template contents of a template element are not children of the element itself." - html.spec.whatwg.org

This means that you can define a template for your Web component and clone it for every instance of your component.

So first you create your template:

const template = document.createElement("template");
template.innerHTML = `
  <style>
    span { color: red; }
  </style>
  <span>Hey There! I am in the template </p>
`;

Then you can clone it's content into your shadow tree:

shadowRoot.appendChild(template.content.cloneNode(true));

As I am sure you noticed, we don't just add the template in the shadowRoot but instead we copy its content. That's because Templates are not rendered so what we need for our component is the content of the template.

Let's implement it in our component:

export class MyComponent extends HTMLElement {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: "open" });

    const template = document.createElement("template");
    template.innerHTML = `
    <style>
        span { color: red; }
    </style>
    <span>Hey There! I am in the template </span>`;
    shadowRoot.appendChild(template.content.cloneNode(true));
  }
}

The important point to keep in mind about templates is that it allows you to declare chunks of HTML in your document that will be rendered and that sole purpose is to be cloned and reused. It is a perfect fit for web components.

You could define it in your index.html since template elements are not rendered in the browser but I would recommend keeping it close to your component declaration to keep your index.html file clean and organize your templates by component.

You can even use the slot Element to make your template more flexible. Those of you who already worked with Vue.js may be familiar with the concept of slot. For the others Slots are like placeholder. They allow the developper to create specific elements that will be placed inside the parents component DOM at predifined emplacement.

Imagine that your component template is a text with holes, each hole has an id. When you add children to your component, you can assign them a slot attribute. This slot attribute allows your component to place them in the "holes " according to their ids (the slot attributes).

If you want to learn more about slot element with web component I would recommend this documentation from MDN.

The addition of those four standards-based technologies is what defines the web component today.

ES Modules

This part is not always explicit in the definition of a web component. It doesn't affect the component on itself but more the way you will be able to use it in a project.

"The ES Modules specification defines the inclusion and reuse of JS documents in a standards-based, modular, performant way." - webcomponents.org

The ES modules should allow developers to use the script tag with the type module. Wich means you can export variables or components from your .mjs files and import it in your .html file within a module script tag.

The ES modules standard allow developers to reuse web component in any HTML file by importing them from an mjs file.

// wc.mjs
export class MyComponent extends HTMLElement {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: "open" });

    const spanElement = document.createElement("span");
    spanElement.textContent = "Hello world";
    spanElement.setAttribute("class", "my-class");

    const style = document.createElement("style");
    style.textContent = ".my-class {color: red;}";

    shadowRoot.appendChild(style);
    shadowRoot.appendChild(spanElement);
  }
}
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>my first custom element</title>
    <style>
      span {
        color: blue;
      }
    </style>
  </head>
  <body>
    <span>Hey! i am just here for the example tho...</span>
    <my-component></my-component>
    <script type="module">
      import { MyComponent } from "./wc.mjs";
      customElements.define("my-component", MyComponent);
    </script>
  </body>
</html>
An MJS file is a source code file containing an ES Module.
You will need a server to be able to fetch with import, as it doesn’t work on the file:// protocol. You can use npx serve to start up a server in the current directory for testing locally.

If you are interested in how the ES module works I strongly advise that you read this article from Lin Clark.

Can I use it?

Now, a lot of developers are still quite reluctant to use web components in their projects. And you can understand why! The lack of common standards implemented by all major browsers since it's creation in the early 2010s has allowed many web component libraries to raise and grow sub-communities that do not share the same opinion on the definition of a web component and its implementation.

On the article Why the React community is missing the point about Web Components by Ben Halpern in November 2018, Dan Abramov comments the following :

[...] The problem is that there's no single "web component community". There are multiple sub-communities. You mention "web component libraries" in the post. These libraries don't agree on a single standard so React would need to pick a side in debates like "how does server rendering work with web components". Whatever we pick will have large downstream effects, and our reluctance to support more has to do with being careful [...] about the semantics — not somehow being at odds with web components per se.

So I think some important points have to be clarified for you to understand the implication of using it in your projects.

Web components are tools

It is important to understand that Web components are not designed to be better than HTML elements. We can consider web components as a tool to extend HTML, not to replace it.

And as we know the same tool cannot be used for all projects. So before adding this tool to your toolbox make sure you really need it or else it might some unecessary weight.

Here are some points that you should consider before jumping in :

  1. It is not that simple to write a web component. It can be quite complex and verbose depending on the scope of your web components. And most of the project with quite a large number of web components have been using some libraries to creates them. webcomponents.org has a page dedicated to those libraries.
  2. Comparing a web component and a react component for example is like comparing apples and oranges. The definition of a web component is not setting limits to what you can put in it. React in its documentation explain it this way:
"React and Web Components are built to solve different problems. Web Components provide strong encapsulation for reusable components, while React provides a declarative library that keeps the DOM in sync with your data. The two goals are complementary. As a developer, you are free to use React in your Web Components or to use Web Components in React, or both."

Understanding and defining what will be the interfaces between web components and the rest of your project's tools is important.

  1. Web component v1 standards are not that old and you probably want your project to work on older browsers versions too. Simply put, you might need polyfills. I will come back to it later on.
  2. One of the down points of the web component often cited is that developers have been struggling to use it with server-side rendering. This is mainly caused by the fact that the contents of ShadowDOM are often missing when the view is delivered to the client. One possibility is to remove the shadow dom from your component like you can see in this article. Other web component advocates found ways to make it work like in this article by Steve Belovarich or this one by Peter Goes. I think you can sum it up by saying that for now there are no standards for server-side rendering with a web component but you can always find a workaround.

Just remember to take all this point in account before jumping in.

Future proof

One of the main arguments of the pro web component community is that web components are future proof. Since it is based on web Standards, it is now supported by all modern browsers and should always be.

I can already hear your ask:

"Alright that's for the future? But what about our beloved past heroes like IE?"

Well you are right to be worried about them and that should be one of your main concerns.

Below are the previously cited technologies browser supports at the current date. If you want the latest data, click on the link below each of them.

can i use custom element v1 can i use custom element v1

can i use custom shadow DOM v1 can i use custom shadow DOM v1

can i use javascript module script tag can i use javascript module script tag

can i use javascript module dynamic import can i use javascript module dynamic import

can i use HTML templates can i use HTML templates

As you can see neither Internet explorer is not supporting any of them neither the early versions of other browsers.

There are polyfills for the web component APIs. You can use the official polyfills.

With those polyfills, nothing you can now develop your future and past proof web component!

Reusability

A web component is easily reusable and is trustworthy. Its strong encapsulation provides security for its data. It also ensures that it will be used the way its designer intended to.

Should I use it?

Based on previous points, there are no specific reasons to not go for it. Still, the Web component isn't mature enough to be trusted for production by most developers. It solves problems but adds others.

For now, the best use the community can agree on for a WC-based project is a design system UI components library. Tesla for example, has crossed the bridge and chosen to make its design system with web components.

Building a sustainable design system can be a real brain teaser for companies. Big companies often use a lot of different technologies and frameworks for all their products.

They reach a point where sustainability becomes a problem in terms of design and technology. They ask themselves those questions :

  • Do we have to create a UI component library for each framework that we use?
  • How can we be sure that we don’t switch framework in the next few years?
  • And If we make sure we don't, aren't we missing an opportunity?

A web component is not based on any framework owned by any company but on standards adopted by all major browsers. You don't have to worry about the hype effect on your chosen technology or framework. This is why web components fit if you decide to create a UI component Library or even a design system.

Of course, you can also use it in any static pages project but I wanted to highlight the one big use where you should consider it.

Conclusion

Now that the standards that define Web components are implemented in every modern browser, they are meant to be part of the future of the web.

As a web developer, it’s time to consider web components as a practical solution for your features and problems.

I would like to quote Serhii Kulykov(@webpadawan) on this article

"Now in 2019, it’s time to give a try to Custom Elements and Shadow DOM. They might not be “the next big thing” by themselves, but rather solid foundation the next big thing could be built upon." - Serhii Kulykov

Of course when the technology will be more mature, accepted and integrated into our projects we will witness the apparition of new ideas and maybe even new standards.

But for now, we are the ones who have to be bold enough to try on our own.

If you are interested in the subject, I recommend the following articles:

The journey of Web Components: wrong ways, lacking parts and promising paths - Serhii Kulykov

Why I don't use web components? - Rich Harris (svelte creator).

I recommend you read the article but most importantly to read its comment. Especially this gist that is a direct answer to the previously mentioned article

Why the React community is missing the point about Web Components - Ben Halpern.

Once again the interesting part is mostly in the comments with a discussion between Dan Abramov et Benny Powers

Josselin d'Espinay

Josselin d'Espinay

Web Developer at Theodo