Front-end development moves at a break-neck pace. This is made evident by the myriad articles, tutorials, and Twitter threads bemoaning the state of what once was a fairly simple tech stack. In this article, I’ll discuss why Web Components are a great tool to deliver high-quality user experiences without complicated frameworks or build steps and that don’t run the risk of becoming obsolete. In subsequent articles of this five-part series, we will dive deeper into each of the specifications.
This series assumes a basic understanding of HTML, CSS, and JavaScript. If you feel weak in one of those areas, don’t worry, building a custom element actually simplifies many complexities in front-end development.
Article Series:
An Introduction to Web Components (This post)
Crafting Reusable HTML Templates
Creating a Custom Element from Scratch
Encapsulating Style and Structure with Shadow DOM
Advanced Tooling for Web Components
What are Web Components, anyway?
Web Components consist of three separate technologies that are used together:
Custom Elements. Quite simply, these are fully-valid HTML elements with custom templates, behaviors and tag names (e.g.
Shadow DOM. Capable of isolating CSS and JavaScript, almost like an
Custom elements contain their own semantics, behaviors, markup and can be shared across frameworks and browsers.
class MyComponent extends HTMLElement {
connectedCallback() {
this.innerHTML = `
Hello world
`;
}
}
customElements.define(my-component, MyComponent);
See the Pen
Custom elements demo by Caleb Williams (@calebdwilliams)
on CodePen.
In this example, we define
Custom elements exist without third-party frameworks and the browser vendors are dedicated to the continued backward compatibility of the spec, all but guaranteeing that components written according to the specifications will not suffer from breaking API changes. What’s more, these components can generally be used out-of-the-box with today’s most popular frameworks, including Angular, React, Vue, and others with minimal effort.
Shadow DOM
The shadow DOM is an encapsulated version of the DOM. This allows authors to effectively isolate DOM fragments from one another, including anything that could be used as a CSS selector and the styles associated with them. Generally, any content inside of the document’s scope is referred to as the light DOM, and anything inside a shadow root is referred to as the shadow DOM.
When using the light DOM, an element can be selected by using document.querySelector(selector) or by targeting any element’s children by using element.querySelector(selector); in the same way, a shadow root’s children can be targeted by calling shadowRoot.querySelector where shadowRoot is a reference to the document fragment — the difference being that the shadow root’s children will not be select-able from the light DOM. For example, If we have a shadow root with a
<#shadow-root>
#shadow-root>
Aside from the pseudo-code of <#shadow-root> (which is used here to demarcate the shadow boundary which has no HTML element), the HTML is fully valid. To attach a shadow root to the node above, we would run something like:
const shadowRoot = document.getElementById(example).attachShadow({ mode: open });
shadowRoot.innerHTML = `
`;
A shadow root can also include content from its containing document by using the
See the Pen
Shadow DOM style encapsulation demo by Caleb Williams (@calebdwilliams)
on CodePen.
HTML templates
The aptly-named HTML element allows us to stamp out re-usable templates of code inside a normal HTML flow that won’t be immediately rendered, but can be used at a later time.
The example above wouldn’t render any content until a script has consumed the template, instantiated the code and told the browser what to do with it.
const fragment = document.getElementById(book-template);
const books = [
{ title: The Great Gatsby, author: F. Scott Fitzgerald },
{ title: A Farewell to Arms, author: Ernest Hemingway },
{ title: Catch 22, author: Joseph Heller }
];
books.forEach(book => {
// Create an instance of the template content
const instance = document.importNode(fragment.content, true);
// Add relevant content to the template
instance.querySelector(.title).innerHTML = book.title;
instance.querySelector(.author).innerHTML = book.author;
// Append the instance ot the DOM
document.getElementById(books).appendChild(instance);
});
Notice that this example creates a template () without any other Web Components technology, illustrating again that the three technologies in the stack can be used independently or collectively.
Ostensibly, the consumer of a service that utilizes the template API could write a template of any shape or structure that could be created at a later time. Another page on a site might use the same service, but structure the template this way:
See the Pen
Template example by Caleb Williams (@calebdwilliams)
on CodePen.
That wraps up our introduction to Web Components
As web development continues to become more and more complicated, it will begin to make sense for developers like us to begin deferring more and more development to the web platform itself which has continued to mature. The Web Components specifications are a set of low-level APIs that will continue to grow and evolve as our needs as developers evolve.
In the next article, we will take a deeper look at the HTML templates part of this. Then, we’ll follow that up with a discussion of custom elements and shadow DOM. Finally, we’ll wrap it all up by looking at higher-level tooling and incorporation with today’s popular libraries and frameworks.
Article Series:
An Introduction to Web Components (This post)
Crafting Reusable HTML Templates
Creating a Custom Element from Scratch
Encapsulating Style and Structure with Shadow DOM
Advanced Tooling for Web Components