⚡IntermediateHTML APIs and Web Components
Web Components Overview
Combine Custom Elements, Shadow DOM, and HTML Templates to build reusable, framework-agnostic UI components.
Overview
Web Components is a suite of browser standards that together enable building fully encapsulated, reusable UI components using only HTML, CSS, and JavaScript no framework required.
The four technologies:
1. Custom Elements: define new HTML tags with custom behavior
2. Shadow DOM: encapsulate styles and markup
3. HTML Templates: define reusable markup fragments
4. ES Modules: (standard JS) import/export components
Web Components work in all modern browsers, can be used in any framework (React, Vue, Angular all consume them), and represent the platform's native component model.
Key Points
- •Web Components combine Custom Elements + Shadow DOM + HTML Templates
- •They work in all modern browsers natively no build step required
- •Framework-agnostic: React, Vue, and Angular can all use web components
- •Use LitElement or Lit (Google) for a lightweight ergonomic base class
- •Web components expose a custom HTML API through attributes, properties, and events
- •The component is a black box: public API is intentional; internals are encapsulated
Examples
A complete Web Component using all three technologies
<!-- HTML Template (defined once, reused) -->
<template id="toast-template">
<style>
:host {
display: block;
padding: 12px 20px;
border-radius: 8px;
font-family: sans-serif;
background: var(--toast-bg, #1f2937);
color: var(--toast-color, white);
}
:host([type="success"]) { background: #065f46; }
:host([type="error"]) { background: #7f1d1d; }
.message { font-size: 0.9rem; }
</style>
<span class="message">
<slot></slot> <!-- projected content -->
</span>
</template>
<script>
class ToastMessage extends HTMLElement {
static get observedAttributes() { return ['type']; }
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
const tmpl = document.getElementById('toast-template');
const clone = tmpl.content.cloneNode(true);
this.shadowRoot.appendChild(clone);
}
}
customElements.define('toast-message', ToastMessage);
</script>
<!-- Usage in HTML -->
<toast-message type="success">Profile saved!</toast-message>
<toast-message type="error">Network error. Please retry.</toast-message>
<toast-message>New message received.</toast-message>The component public API contract
<!-- Public API: attributes, properties, events, and slots -->
<!-- 1. Attributes (HTML API) -->
<my-rating value="4" max="5" readonly></my-rating>
<!-- 2. Properties (JavaScript API) -->
<script>
const rating = document.querySelector('my-rating');
rating.value = 3; // set property
console.log(rating.value); // read property
</script>
<!-- 3. Custom Events (event API) -->
<my-rating onratingchange="handleChange(event)"></my-rating>
<!-- Inside component: this.dispatchEvent(new CustomEvent('ratingchange', { detail: { value: 3 } })) -->
<!-- 4. Slots (content API) -->
<user-card>
<img slot="avatar" src="user.jpg" alt="User avatar">
<span slot="name">Alice Smith</span>
</user-card>Common Mistakes
- Building complex web components from scratch use Lit or similar library for production
- Not providing a clear public API (attributes, properties, events) internal details become hard to use
- Forgetting that Shadow DOM breaks native form association need ElementInternals for custom form elements
Note
Web Components shine for design system tokens: a <ds-button>, <ds-badge>, or <ds-card> component works identically in React, Vue, plain HTML, and legacy jQuery apps. Build the component library once; deploy everywhere.
Exercises
Write HTML to solve each exercise. Use Preview to see it render in a live iframe, then mark as done.
Loading editor…
Completed: 0 / 5 exercises
Knowledge Check
Question 1 of 4