DEV Community

Cover image for How To Build Custom, Reusable Web Components From Scratch
Kingsley Ubah
Kingsley Ubah

Posted on • Edited on • Originally published at letsusetech.com

How To Build Custom, Reusable Web Components From Scratch

In HTML, there are so many elements we can use to mark up our web page. But do you know that you can also create your own custom web component?

The biggest benefit of creating your own custom web components is that you get to define its own built-in styling and functionality. In addition, you can easily reuse it at various places on your web page.

This article talks about the basics of creating a web component. By the end of this article, you’ll have all the information you need to start creating your own web component from scratch.

Sidenote: If you’re new to learning web development, and you’re looking for the best resource to help with that, I strongly recommend HTML to React: The Ultimate Guide.

Setting up the web page

Create a file named index.html in an empty directory. Then open it with a file editor and paste in the following code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="main.css">
    <script type="module" src="main.js"></script>
    <script type="module" src="bigbang.js"></script>
</head>
<body>
    <header>
        <h1>Basic Web Component</h1>
    </header>
    <main>
        <h2>Main area</h2>
        <h3>Web component below</h3>
    </main>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

This is a web page with a header section and a main section, with each section housing different levels of title headings.

Each one of these tags has a semantic meaning behind it. In other words, they clearly describe their meaning to both the browser and the developer.

These elements all have built-in functionality and styling associated with them by the browser.

Web components, on the other hand, are your very own HTML elements. When you’re creating a custom element, you have to define how you want to element to look and behave.

Let’s see how.

Creating a basic web component

If you check the section of your HTML markup in index.html, you can see that we’re importing a JavaScript module named bigbang.js.

This file is going to contain all the code responsible for creating our custom web component/element.

Now open the file in a text editor and paste in the following code:

class BigBang extends HTMLElement {
    constructor() {
        super();
    }    
}

window.customElements.define('big-bang', BigBang)
Enter fullscreen mode Exit fullscreen mode

Here defining a class that extends the HTMLElement class. Inside this class is where you’ll place all the code responsible for building the element. On the last line, we passed this class as the 2nd argument to the define() method of customElements, with the first argument being the tag name of the element.

This means that you could now use the custom element in HTML like this:

<big-bang></big-bang>
Enter fullscreen mode Exit fullscreen mode

Keep in mind that there has to be a hyphen in your web component’s tag name. This helps to make it stand out as a custom component.

Now, inside the constructor method of the BigBang class, one of the things that you’ll need is a shadow root. This is like a container that’s going to hold your component:

class BigBang extends HTMLElement {
    constructor() {
        super();
        const shadowRoot = this.attachShadow({mode: closed})
    }    
}
Enter fullscreen mode Exit fullscreen mode

Because custom components can contain styling, scripts, and functionality, we don’t want those things spilling out and impacting the rest of the page. In other words, we don’t want to define CSS here and have it change the way that the rest of the page looks.

The other way is fine, though. The rest of the page can have its styles cascade down into our component.

The shadow root creates some sort of sandbox around our element. The ‘open’ value means that the parent script (i.e. main.js) can actually do things like document.querySelector(), get inside of our custom component, and see what the actual tags we’re using inside it are.

If it’s closed, they can’t.

Next, inside the constructor, create a div element with some text in it, then attach the div to the shadow root:

class BigBang extends HTMLElement {
    constructor() {
        super();
        const shadowRoot = this.attachShadow({mode: 'closed'})
        const div = document.createElement('div')
        div.textContent = 'Big Bang Theory'
        shadowRoot.append(div)
    }        
}
Enter fullscreen mode Exit fullscreen mode

With this code, you have just created a custom element.

To use the element in your markup, go into index.html and add the following element inside the H2 tag and under the main tag:

<big-bang></big-bang>
Enter fullscreen mode Exit fullscreen mode

If you open the index.html document in a web browser, you’ll find the custom element rendered on the page (which renders the text, ‘Big Bang Theory’):

Screenshot of custom web component

The component also has text built into it, so you don’t have to pass anything between opening and closing tags as you would with most other HTML elements.

This leads to the next question: How do we get things nested inside our custom web element? The answer is slots!

Introducing Slots

Slots allow you to “slot” content into your custom element by nesting it within the opening and closing tags of your element in HTML. Let’s see a bit of a demonstration.

At the top of the bigbang.js file, let’s first create a template element:

const template = document.createElement('template')
template.innerHTML = `
    <div>
        <h2>Big Bang Theory</h2>
    </div>
`
Enter fullscreen mode Exit fullscreen mode

Then inside the constructor, replace the entire code for creating and inserting the div element with that of the template:

class BigBang extends HTMLElement {
    constructor() {
        super();
        const shadowRoot = this.attachShadow({mode: 'closed'})

        /* 
           Remove this:
           const div = document.createElement('div')
           div.textContent = 'Big Bang Theory'
           shadowRoot.append(div)
        */

        let clone = template.content.cloneNode(true)
        shadowRoot.append(clone)
    }        
}
Enter fullscreen mode Exit fullscreen mode

We basically cloned everything in the template and appended it to the shadow root. If you check your browser, you should find the text appearing on the page as an H2.

Now is the time to slot in some actual content. Inside the markup in index.html, put the following content between the <big-bang> tags:

<big-bang>
  <h2 slot="title">Characters</h2>
  <ul slot="list">
    <li>Leonard</li>
    <li>Kingsley</li>
    <li>Sarah</li>
    <li>John</li>
  </ul>
</big-bang>
Enter fullscreen mode Exit fullscreen mode

If you want to be able to use nested content, you need to give them a slot name as we did above.

Now back in your template, in order to include these elements inside the template, you need to add the tag for each of them:

const template = document.createElement('template')
template.innerHTML = `
    <div>
        <h1>Big Bang Theory</h1>
        <slot name="title">Default text if no title slot</slot>
        <slot name="list"></slot>
    </div>
`
Enter fullscreen mode Exit fullscreen mode

We also added a default title in case none is provided in the HTML. This is the result (slots highlighted in red):

Screenshot of web component with slot

Conclusion

That’s all! Just with HTML and a few lines of JavaScript, you were able to create your very own web component.

Now there is more to this. We can define styles, behaviors, and functionality. I’ll be tackling each of these in the coming articles. So make sure to subscribe so you don’t miss them.

Want to collaborate with me? Fill out this form.

Top comments (0)