By WebNest Studio Engineering Team
HTML Tutorial
Web Components Basics
Web Components are a set of native browser APIs that let you build your own custom, reusable HTML elements — things like <my-tabs> or <user-card> — that encapsulate their own markup, styling, and behavior. Unlike a React component or a Vue component, a Web Component is not tied to any particular framework: it works the same way in plain HTML, in a React app, in a Vue app, or anywhere else, because it is built directly on browser standards rather than a library.
This makes Web Components especially useful for design systems and shared UI libraries that need to work across teams using different frontend frameworks, or across an organization that has not standardized on one framework at all.
Why it matters
Web components let you define your own HTML elements, with their own markup and styles that do not leak in or out, using only what the browser provides. They work in any framework or with none, which is why design systems at large companies are often built on them. They also explain ideas, such as components and encapsulation, that React and Vue borrow.
The Three Core Technologies
"Web Components" is really an umbrella term for three separate browser specifications that are typically used together.
- Custom Elements — the API (
customElements.define()) that lets you register a new HTML tag name backed by a JavaScript class, with its own lifecycle callbacks. - Shadow DOM — an encapsulated, isolated DOM subtree attached to an element; CSS and querySelector calls from outside cannot accidentally reach into it, and its internal styles cannot leak out.
- HTML Templates — the
<template>tag, which holds inert markup that is parsed by the browser but not rendered until you clone it into the page with JavaScript, making it an efficient stamp for repeated structure.
Defining a Custom Element
You create a custom element by extending HTMLElement (or a built-in element type), then registering it with customElements.define('tag-name', ClassName). Custom element tag names must contain a hyphen (e.g. user-card, not usercard) specifically so the browser can distinguish them from current and future standard HTML tags. Lifecycle callbacks like connectedCallback() run automatically when the element is inserted into the page.
Why This Matters for Framework-Agnostic UI
Because a registered custom element behaves like any other native HTML tag, you can drop <user-card> into a static HTML page, a React JSX tree, or an Angular template, and it will render and function identically in all three — the browser handles it directly, with no adapter code required. This is why component libraries aimed at multiple teams (for example, a company-wide design system) are increasingly built as Web Components rather than framework-specific components.
Example
A simple custom element with encapsulated Shadow DOM styling
<!-- index.html -->
<user-card name="Asha Patel" role="Frontend Engineer"></user-card>
<script>
class UserCard extends HTMLElement {
connectedCallback() {
const name = this.getAttribute('name');
const role = this.getAttribute('role');
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
.card { border: 1px solid #ccc; border-radius: 8px; padding: 12px; font-family: sans-serif; }
.name { font-weight: bold; }
</style>
<div class="card">
<div class="name">${name}</div>
<div class="role">${role}</div>
</div>
`;
}
}
customElements.define('user-card', UserCard);
</script>
The page renders a bordered, rounded card showing "Asha Patel" in bold above "Frontend Engineer." The card's internal .card and .name CSS classes are scoped inside the Shadow DOM, so they never clash with any .card or .name class defined elsewhere on the page.
Common Mistakes
- Forgetting the required hyphen in a custom element tag name (e.g. writing "usercard" instead of "user-card") — the browser will refuse to register it.
- Expecting global page CSS to style inside a Shadow DOM automatically — encapsulation means outside styles do not apply unless explicitly passed in (for example, via CSS custom properties).
- Doing heavy setup work in the class constructor instead of connectedCallback — the constructor runs before the element is guaranteed to be attached to the document, which can cause subtle bugs.
- Assuming Web Components replace frameworks entirely — they are a low-level tool for building interoperable pieces, and many teams still use a framework on top for routing, state management, and data fetching.
Exercise
Define a custom element named hello-card. It should attach an open shadow root and render a paragraph that reads Hello, followed by the value of its name attribute, in blue text styled inside the shadow DOM.
Expected result: the page shows Hello, Asha in blue, and the ordinary paragraph below it keeps its default colour.
<hello-card name="Asha"></hello-card>
<p>This paragraph is outside the component.</p>
<script>
class HelloCard extends HTMLElement {
connectedCallback() {
// TODO: attach an open shadow root
// TODO: set its innerHTML to a style rule and a paragraph using the name attribute
}
}
// TODO: register the element under the name "hello-card"
</script>
Show hints
- The shadow root is created with
this.attachShadow({ mode: "open" }). - Register the class with
customElements.define("hello-card", HelloCard). The name must contain a hyphen.
Show solution
<hello-card name="Asha"></hello-card>
<p>This paragraph is outside the component.</p>
<script>
class HelloCard extends HTMLElement {
connectedCallback() {
const shadow = this.attachShadow({ mode: "open" });
const name = this.getAttribute("name");
shadow.innerHTML = "<style>p { color: blue; }</style><p>Hello, " + name + "</p>";
}
}
customElements.define("hello-card", HelloCard);
</script>
Quick quiz
1. Which rule applies to the name of a custom element?
- A. It must start with a capital letter
- B. It must contain a hyphen
- C. It must end in -element
- D. It must be a single word
Show answer
B. The hyphen keeps custom names from clashing with present and future built-in elements.
2. What does the Shadow DOM provide?
- A. Server-side rendering
- B. Faster rendering
- C. Encapsulation: the component's markup and styles are isolated from the rest of the page
- D. A database in the browser
Show answer
C. Styles inside do not leak out, and page styles do not reach in.
3. When is connectedCallback called?
- A. When the class is defined
- B. When the page is closed
- C. When an attribute changes
- D. Each time the element is added to the document
Show answer
D. It is the usual place to render the content of the component.
Interview questions
What are the three technologies that make up web components?
Show answer
Custom Elements, the JavaScript API for defining new HTML elements and their behaviour through lifecycle callbacks. Shadow DOM, which attaches a separate, encapsulated DOM tree to an element so its markup and styles are isolated. And HTML templates, the template and slot elements, which hold markup that is not rendered until it is used and mark where content supplied by the user of the component is placed.
How do web components compare with framework components such as React?
Show answer
Web components are a browser standard: they need no library, and an element defined once can be used in plain HTML or inside any framework. Framework components offer a richer development model, with declarative rendering, state management and a large ecosystem, but only work within that framework. Web components suit shared design systems and widgets that must work everywhere; frameworks suit building whole applications. The two can be combined.
Key Points to Remember
- Web Components combine three standards: Custom Elements, Shadow DOM, and HTML Templates.
- customElements.define("tag-name", ClassName) registers a new element; tag names must include a hyphen.
- Shadow DOM encapsulates markup and styles so they cannot leak in or out of the component.
- Because they are native browser features, Web Components work identically across any framework or no framework at all.
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Shows a live preview of your HTML, CSS and JavaScript. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering