How to create Web Components by a project
This is a how-to about creating native web components. I will skip the style part in the explanation because it’s not so relevant and the project would still work without it, albeit in a less aesthetically pleasing way. We’ll start by explaining a little bit about what web components are, then we’ll look at an step-by-step example that can be viewed with this repo or in this live demo
What are web components?
To use web components it’s important to understand what they are, according to MDN, web components are based on three main technologies, which can be used together to create versatile custom elements with encapsulated functionality that can be reused wherever you like without fear of code collisions.
Let’s introduce some web components definitions:
Custom elements: A set of JavaScript APIs that allow you to define custom elements and their behaviour.
Shadow DOM: A set of JavaScript APIs for attaching an encapsulated “shadow” DOM tree to an element — which is rendered separately from the main document DOM — and controlling associated functionality. Elements can be scripted and styled without the fear of collision with other parts of the document.
HTML templates: The <template> and <slot> elements enable you 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.
“W3C” includes:
ES Module specification: defines the inclusion and reuse of JS documents in other JS documents.
Let’s start the project.
The idea of this project is to create a couple of components that are connected between them, not only to generate a component but also to see how you can pass data from one to another. In the end, we’ll have four buttons that, when you click on them, show you some movie quotes.
So let’s start by creating an almost empty project with index.html, style.css, and main.js files.
Our second step would be to create a container component into our main.js file to be able to pass props between its children.
Here we are already using the three main technologies. First of all, we are creating a class that extends HTMLElement, an API that represents an element in the HTML document tree. HTMLElement is the base type for HTMLDivElement, HTMLSpanElement, HTMLImageElement and many others and allows us to create our own HTMLElements. Custom elements allow us to extend existing (native) HTML elements as well as other custom elements.
Then we use the constructor to set some initial state, event listeners, and create the shadow DOM. An important thing here is { mode: ‘open’ }. What is this for? Well, it’s to avoid public access to the nodes within the shadow tree. So try and ‘Close the DOM!’ (this is funnier if you imagine it said by Hodor). However, both open and closed modes have the same benefits: an isolated DOM, scoped CSS, and a declarative, markup-based API. The problem is that it can be perceived as a ‘security’ feature. So maybe it is better if we keep the DOM open for now. If you’d like to find out more about it, this article is really interesting.
Last, but not least important, we are using the <slot> element from HTML templates to include, in the near future, each of our inner components.
Ok, now we already have our first web component, the hardest part… but let’s dig a little deeper. We are going to create our NavBar component.
There are two new things here. First, we are generating the navbar options dynamically, so we’ll have as many tabs as objects in our data file. Second, we are handling the onclick event from our new option_button. This is important because it allows us to set the selected attribute in our display component.
So finally we need to create the Display component:
In the Display component, we need to recover our set attribute and we will do it with static get observedAttributes(), this method should return an array of strings where each string is the name of the attribute you wish to observe. It is also aware of the changes in these attributes with the attributeChangedCallback() method. In our case, selected, this will only work with the attributes that are being observed. Finally we call the setSelected function that will return one random item inside the array in the selected object from the data file.
Conclusion
In conclusion, I would say that it is quite easy to get started with web components, especially if you have had some previous experience with frameworks such as React, Vue or any others. It would be a good launchpad to start using web components alongside the framework that you are already using as they have complementary purposes. While frameworks allow you to handle state, custom elements are stateless and provide your components with encapsulation, they also make your components agnostic which can be a important aspect of a good Design System but we will talk about this in another post.
There are also a bunch of libraries that can help you to improve your web components as much as you want:
- Stencil.js, open-source.
- Polymer, build by Google.
- And many others that you can check out here: https://www.webcomponents.org/libraries.
Hope you enjoyed this article and feel free to clone or improve my project here: https://github.com/IrisCZ/web_components_the_beginning/
Or if you just want to see it work: https://iriscz.github.io/web_components_the_beginning/
Acknowledgments: many thanks to Sergio Espeja and Bethany Solberg, this article wouldn’t be the same without them.