DEV Community

Cover image for Where to begin building Web Components? - The Basics
Alan Dávalos
Alan Dávalos

Posted on

Where to begin building Web Components? - The Basics

Introduction

If you're reading this you've probably at least heard a bit about Web Components, a set of web standards that allow us to create our own reusable UI components that can be used in any kind of web application and natively supported in all modern browsers.

What you might not know is where to start, because if there's anything amazing yet terrible about the web development landscape is that where there's an API there will be a bazillion abstractions to "Make it Simpler™".

And of course, Web Components are not the exception, last time I checked I could find around twenty-something different libraries that provide some sort of abstraction to help you create a Web Component.

So, for anyone just trying to begin building Web Components just even finding out where to start is pretty difficult, which is why I'm here.

Throughout this series of articles (yes, it's a series!), I'll cover the following points:

  1. The basics of the Web Components standard: I'll be covering these in this article 😉
  2. The types of approaches different libraries take to help you be more efficient when creating Web Components: I'll cover each type in a separate article and try to give a brief introduction of most of the libraries I could find that follow each type

Just bear in mind, this isn't a tutorial per se, I won't be explaining how to build Web Components with each library, I believe that's what the docs for each library are there for to begin with.

The main purpose of this article is to try to help developers who are just starting with Web Components find a way of building Web Components that they feel comfortable with. 😊

To quote Justin Fagnani, one of the members of the Polymer Project which greatly contributed in pushing the Web Components standard:

[...] the big idea with web components is that it shouldn't matter which or even if an approach (to create Web Components) becomes more popular. As long as components interoperate we all win.

So, let's stop with the introduction and start with the juicy stuff.

What's actually needed to create a Web Component?

To avoid repeating what many other articles have mentioned, I won't explain all the standards that make up Web Components, but if you need a reminder I recommend you to check this MDN article.

Now, knowing what the standards are about is cool and all, but what does a vanilla Web Component actually look like?

Here's some sample code for a simple Hello World component, don't worry if you don't fully understand what everything is, we'll go through it in more detail later on. 😉

const template = document.createElement("template");
template.innerHTML = `<div>Hello <span class="name"></span></div>`;

class MyGreeting extends HTMLElement {
  constructor() {
    super();
    this.name = "World";
  }

  // Start - Standard Lifecycle Callbacks
  // This gets triggered when the component first is appended to the document
  connectedCallback() {
    if (!this.shadowRoot) {
      this.attachShadow({ mode: "open" });
      this.shadowRoot.appendChild(template.content.cloneNode(true));
    }
    this._nameSpan = this.shadowRoot.querySelector(".name");
    this._nameSpan.textContent = this.name;
  }
  // This defines which attributes will trigger a callback when they get set on the component
  static get observedAttributes() {
    return ["name"];
  }
  // This callback will get triggered when one of the observedAttributes gets changed
  attributeChangedCallback(attr, oldVal, newVal) {
    if (oldVal !== newVal) {
      this[attr] = newVal;
    }
  }

  // End - Standard Lifecycle Callbacks

  set name(value) {
    this.safeSetAttribute("name", value);
    if (this._nameSpan) {
      this._nameSpan.textContent = value;
    }
  }

  get name() {
    return this.getAttribute("name");
  }

  // a helper function to prevent an endless loop on attribute assignment
  safeSetAttribute(attr, value) {
    if (this.getAttribute(attr) !== value) {
      this.setAttribute(attr, value);
    }
  }
}

window.customElements.define("my-greeting", MyGreeting);
Enter fullscreen mode Exit fullscreen mode

In this simple code you can see all the Web Components standards in action:

  1. We create a <template> that will be used for our component.
  2. We create a class that extends the native HTMLElement class which will be registered in the window level CustomElementRegistry. This will make all the <my-greeting> tags rendered use our class to know what to render.
  3. Our class contains some of the custom elements lifecycle callbacks which mostly help us to know when to setup, destroy, or update our component.
  4. We use the attachShadowRoot function to create the Shadow DOM tree for our component.

You might think that this code is slightly too cumbersome for what seems to be too little.

And you're right in thinking that, the Web Components standards, at least in their current form, are low-level standards that require you to write code for things that are needed pretty much for all use cases.

Let's "Make it Simpler™"

This is where the abstractions I mentioned before come in, all of them basically aim to solve the pain points of working with each standard by:

  1. Providing a rendering engine that removes all manual DOM manipulation
  2. Either extend, wrap, or compile to a class that can be registered in the CustomElementRegistry
  3. Extend the native lifecycle callbacks and sometimes add library-specific callbacks that can help with more use cases like state management and many others.
  4. Handle the creation of the Shadow DOM tree with fallbacks either to polyfills or no Shadow DOM at all.

All of these abstractions generally make the overall development experience way more pleasant than working with vanilla web components.

And what's even better is that since most of the final heavy lifting is done by the actual standards the majority of the libraries covered throughout this series won't add even 10kB to your final bundle (after minify/gzip)! 💪

What's Next?

So far, I (hopefully) helped you understand what it takes to create a Web Component and why you probably want to use a library to help you out having a nice experience while doing so.

But let's not forget about the original objective, I'm supposed to be playing cupid here and match you with your perfect library. 💘

And while I mentioned many abstractions the libraries provide over the standards, I believe the one that has the biggest effect on how you actually end up writing the code for your component is the way to define the "class" for your component.

Like I mentioned above, most libraries fall into one of three patterns:

  1. They provide a class that extends HTMLElement and adds the extra features onto it so that you extend that new class in your code.
  2. They provide a function that will create a class for your component with both the extra features and your component code when called.
  3. They provide tools that can be used to compile your code, usually written in a proprietary syntax, into a class for your component with both the extra features and your component called.

In the following articles, I'll go into more detail about how each pattern works in detail and try to briefly introduce as many libraries as I can that fall into that pattern.

Thank you very much for reading this article to the end, I hope you liked it and keep reading the other articles for this series.

Feel free to leave a comment with any questions or suggestions you have for the rest of the series, especially on what kind of data you would like to hear about from the libraries I'll be introducing.

Oldest comments (0)