DEV Community

Cover image for Exploring Web Components: Building Reusable UIs for Web Development πŸŒπŸ‘¨β€πŸ’»
Sudhil Raj K
Sudhil Raj K

Posted on • Edited on

Exploring Web Components: Building Reusable UIs for Web Development πŸŒπŸ‘¨β€πŸ’»

Even though Web Components have been around for a while, I've never used them in my projects, and I've never seen my colleagues use them either! Have you ever tried using Web Components?

Web Components emerged around 2011 as a collection of web technologies aimed at making reusable custom elements for web development. These components include Custom Elements, Shadow DOM, HTML Templates, and HTML Imports (though the latter is now deprecated). They've evolved over time through collaboration between browser vendors, web standards bodies, and the web development community. With Web Components, we can create encapsulated custom elements with specific functionality and use them in our web applications to promote reusability and maintainability.

Concepts and usage πŸš€βœ¨

As developers, we're aware that reusing code whenever possible is beneficial. However, custom markup structures, like complex HTML for custom UI controls, have traditionally posed challenges for reuse. Using them multiple times can clutter our page with tangled HTML, styles, and scripts if not managed carefully.
Web Components aim to address these challenges by offering three main technologies that work together to create flexible custom elements. These elements encapsulate their functionality, allowing for easy reuse throughout our projects without worrying about code conflicts.

1) Custom elements πŸ’‘

A set of JavaScript APIs that allows us to define custom elements and their behavior, which can then be used as desired in our user interface.

  • Custom Elements enable developers to define their own custom HTML elements with associated behavior.
  • We can create new HTML tags that encapsulate specific functionality and use them just like standard HTML elements.
  • Custom Elements are defined using the class syntax and extending the HTMLElement class.
class MyComponent extends HTMLElement {
  constructor() {
    super();
    // Initialization code
  }
}

customElements.define('my-component', MyComponent);
Enter fullscreen mode Exit fullscreen mode

2) Shadow DOM πŸ’‘

The Shadow DOM API comprises JavaScript APIs that allows us to attach an encapsulated "shadow" DOM tree to an element. This shadow DOM tree is rendered independently from the main document DOM, keeping the element's features private. By doing so, we can script and style the element without worrying about conflicts with other parts of the document. This encapsulation ensures that the element's functionality remains contained and isolated, enhancing maintainability and reducing the risk of unintended interactions.

  • The Shadow DOM allows us to encapsulate the internal structure and styling of a custom element.
  • It provides a scoped DOM tree, isolated from the rest of the document, preventing styles and scripts from leaking into or out of the component.
class MyComponent extends HTMLElement {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });
    shadowRoot.innerHTML = '<p>This is inside the shadow DOM</p>';
  }
}

customElements.define('my-component', MyComponent);
Enter fullscreen mode Exit fullscreen mode

3) HTML templates πŸ’‘

The <template> and <slot> elements enable us to write markup templates that are not displayed in the rendered page. These can then be reused multiple times as the basis of a custom element's structure.

  • HTML Templates allow developers to declare fragments of HTML that can be cloned and inserted into the DOM dynamically.
  • Templates are often used in conjunction with Custom Elements to define the internal structure of a component.
<template id="my-template">
  <style>
    /* Styles for the template */
  </style>
  <div>
    <p>This is a template content.</p>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

4) HTML Imports (deprecated)

HTML Imports were initially part of the Web Components specification but have been deprecated in favor of using ES6 modules and other module systems. Developers are encouraged to use modern module systems and tools for managing dependencies.

So the basic approach for implementing a web component generally looks something like this:

  1. Create a class in which we specify our web component functionality, using the class syntax.
  2. Register our new custom element using the CustomElementRegistry.define() method, passing it the element name to be defined, the class or function in which its functionality is specified, and optionally, what element it inherits from.
  3. If required, attach a shadow DOM to the custom element using Element.attachShadow() method. Add child elements, event listeners, etc., to the shadow DOM using regular DOM methods.
  4. If required, define an HTML template using <template> and <slot>. Again use regular DOM methods to clone the template and attach it to our shadow DOM.
  5. Use our custom element wherever we like on our page, just like we would any regular HTML element.

Working Example πŸ‘¨β€πŸ’»πŸ”₯

We'll define a custom HTML element called <my-button> that displays a button with customizable text.

<!-- button.js -->
const template = document.createElement("template");
template.innerHTML = `
   <style>
      /* Styles for the button */
      :host {
         display: inline-block;
      }
      button {
         padding: 10px 20px;
         font-size: 16px;
         background-color: #ffa500;
         color: #fff;
         border: none;
         border-radius: 5px;
         cursor: pointer;
      }
      button:hover {
         background-color: #f5ab25;
      }
   </style>
   <button><slot></slot></button>`

export class MyButton extends HTMLElement {
   constructor() {
      super();
      // Create a shadow root
      const shadowRoot = this.attachShadow({ mode: 'open' });
     // Clone the template content and append it to the shadow DOM
      const instance = template.content.cloneNode(true);
      shadowRoot.appendChild(instance)
   }

   // connectedCallback(){ }
}

customElements.define('my-button', MyButton); //NOTE: The name must have atleast one hyphen(-) in it.
Enter fullscreen mode Exit fullscreen mode

Now, we can use the <my-button> custom element in any HTML file.

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
   <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Custom Button - Web Component</title>
      <script defer type="module" src="button.js"></script>
   </head>
   <body>
      <my-button>Hi, I am Custom Button!</my-button>
   </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Open index.html in a web browser, and we should see a button labeled "Hi, I am Custom Button!". This button is rendered using the <my-button> custom element defined in button.js.

Final Output

Final Thoughts πŸ€”πŸ’‘

In summary, traditional web development using plain HTML involves manual coding and styling of UI elements, whereas Web Components offer a structured and modular approach. With encapsulated behavior and styling, thanks to features like shadow DOM, Web Components lead to more maintainable and scalable codebases. Additionally, they provide a standardized method for creating reusable and interoperable components across various frameworks and libraries. While adoption may vary, Web Components offer powerful tools for building modular and maintainable web applications.

Happy Coding! πŸš€πŸ‘¨β€πŸ’»βœ¨

Top comments (3)

Collapse
 
prakhart111 profile image
Prakhar Tandon

Hmm cool, but should we use React/Next instead of manually configuring stuff? Or if you want something super light, you can checkout VanJS

Collapse
 
sudhil profile image
Sudhil Raj K

Hey Prakhar, thanks for your input! We can use React/Next β€” they do offer powerful features and a vibrant ecosystem, making them a solid choice for complex applications and teams familiar with their conventions. I'm actually using them myself.

Regarding Web Components, they provide a native, browser-supported approach to building reusable components, which promotes interoperability and encapsulation. They're particularly useful for projects that require a lightweight, framework-agnostic solution, or when custom elements offer specific advantages. While we're not currently using them, it's definitely valuable to have an understanding of their capabilities.

I'll take a look at VanJS as well β€” thanks for the recommendation! Thanks.🀝

Collapse
 
tijogomez profile image
Tijo B Gomez

Cool stuff !