Web Components in a Nutshell

Fundamentals of Native Browser Web Components and How to Create Them

Thivagar Mahendran
Level Up Coding

--

In web development, code reusability has become a strong focus. As a developer, we may have experienced a scenario where we had to use a code segment which represents custom UI controls in multiple places. If we fail to write them carefully, it may make our entire code structure unmanageable. Web components provide a native API to build reusable UI blocks.

What are Web Components?

Web components are a collection of lower-level browser APIs that helps us to create reusable, encapsulated custom HTML UI elements. Web components are considered much better because they can be created by using any library or framework, and you can start writing your own web component using vanilla JavaScript immediately.

One advantage of using web components is that they are available for all browsers except Microsoft Edge, but we don’t need to worry about it because we have Polyfills to resolve that issue.

Web components consist of three major technologies. They are the main pillars that can be used as APIs to build web components.

  • Custom Elements
  • Templates
  • Shadow DOM

Let’s take deep dive into these technologies.

1. Custom Elements

These are a set JavaScript APIs which help you to create HTML elements of your own and to control your DOM and its behaviour. We can build their hierarchy and dictate how they react to behavioural changes. For example, you can create an element like this <my-element></my-element>.

2. Templates

Templates are a user-defined markup that is only rendered upon the page load. Later these can be reused several times by creating an instance of the component.

3. Shadow DOM

The Shadow DOM is a combination of JavaScript APIs to connect with the encapsulated DOM. This will be rendered separately from the main Document Object Model and their behaviour features will be kept private so that the code segment will not collide with other parts of the code structure. The CSS and JavaScript can be isolated almost like an <iframe> tag using Shadow DOM.

Lifecycle Callbacks

Lifecycle callbacks are functions that are defined inside the custom element’s class definitions. They have their own uniquely defined purpose. They are used to manipulate the behaviour of our custom elements.

  • connectedCallback: This particular function will be invoked when our custom element gets connected to the DOM initially.
  • adoptedCallback: This function will be called when our custom element has been moved to a new document.
  • attributeChangedCallback: If there is a change in our custom element’s attribute, such as an attribute being changed, added, or removed, this particular function will be invoked.
  • disconnectedCallback: This particular function is invoked when our custom element is disconnected from the DOM.

Now Let’s see how we can create a web component using vanilla JavaScript. By doing this you will get a clear picture of web components.

What we are going to build?

We are going to build a web component that contains a current trending gif image. We are going to use the Giphy API to fetch the gif. Your code structure would look like this at the end of the implementation.

--index.html
--card.js
--services.js

Let’s Get Started!

credits: Giphy.com

First, we must create a class that contains the behaviour of the web component that we are going to build. Create a file called card.js and create a class like below.

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

//Your implementaion goes here

}
}

In the constructor of the class, you need to attach the shadow root of the Shadow DOM to the current HTML element of the document by using Element.attachShadow() method. Then we need to create an HTML template using <template> tag in our index.html file. The template would look something like this

<template id="card-view">
<h1>Web Component</h1>
<p id="card-title">Example</p>
<img id="gif-view"/>
</template>

After adding this in our index.html file we can use DOM methods to clone the above template and attach it to our Shadow DOM. This should be written inside the constructor.

class CardComponent extends HTMLElement {
constructor (){
super();
const shadow = this.attachShadow({mode: 'open'});

// Clone the template so that it can be attched to the
shadowroot

const template = document.getElementById('card-view');
const templateInstance = template.content.cloneNode(true);
shadow.appendChild(templateInstance);
}
}

As I mentioned earlier, we should also write a function to fetch the gif from Giphy API. From the API we are going to get a current trending gif and its title provided by the uploader of that particular gif. Before we start writing the function, create a separate file called services.js to place the URL and API key. Create the file and add the following code with your API key from Giphy API

const API_KEY = '*YOUR_API_KEY*';const url = `http://api.giphy.com/v1/gifs/trending?api_key=` + API_KEY + `&limit=1`;export {API_KEY, url}; // export the url so that i can be used extrnally.

After creating the services.js file, add the below code to your card.js file at the top so that you can use the URL to fetch the gif.

import { url } from './services.js';

To get the API key of your own visit the following site. https://developers.giphy.com/

Let’s jump back to our card.js file and add the following functions.

render(shadowElem, data){
const shadowRoot = shadowElem.shadowRoot;
shadowRoot.getElementById('card-title').innerHTML = data.name;
shadowRoot.getElementById('gif-view').src = data.url;
}
async fetchFromGiphy (){
const res = await fetch(url);
const json = await res.json();
const gifUrl = json['data']['0'].images['fixed_height_small'].url;
const gifName = json['data']['0'].title;
const gifObject = {
name: gifName,
url: gifUrl
}
return gifObject;
}

Let me explain these functions.

fetchFromGiphy(): This function fetches the trending gif and the title of the specific gif and returns them both as an object using async/await.

render(): This function is used to inject the values inside the Shadow DOM’s elements.

Next, let’s have these functions be called in a lifecycle callback. Actually, we need the above two functions to be invoked when our custom elements connect to the DOM. We have the connectedCallback() function to achieve this.

async connectedCallback (){
this.gifObj = await this.fetchFromGiphy();
this.render(this, this.gifObj);
}

Finally, let’s define our custom element using the customElements.define() function. When defining a custom element there are some ground rules that we should keep in mind. The first parameter of the define() function should be a string which represents our custom element’s name. They can not be a single word, so it should have a - character in them. The second parameter would be our class object which defines the behaviour of our element.

customElements.define(‘card-component’, CardComponent);

Now that you have defined your component, add your card.js file to your index.html file. You can use the following element <card-component> anywhere in your HTML document. At the end of this journey, your index.html file would be like below.

<!DOCTYPE html>
<html lang="en">
<head>
<title>Web Component</title>
</head>
<body>

<template id="card-view">
<h1>Web Component</h1>
<p id="card-title">Example</p>
<img id="gif-view"/>
</template>

<card-component></card-component>
<script src="./card.js" type="module"></script>
</body>
</html>

To run this, you need a server. Install static-server globally from your command line tool

npm install -g static-server

Run the static-server command from your workspace of the web component project.

static-server

Well, congratulations! You have your own component now.

credits: Giphy.com

Conclusion

This article summarizes the basics of web components. This is the theory and implementation of web components. Web components are incredibly useful since they help in code reusability. You can check out the whole code for this little project here.

--

--