Phase 3 · Browser DevelopmentModule 23~40 min read

Web Components

Build reusable native UI components with custom elements, templates, and Shadow DOM.

What you'll learn

Web Components package native reusable UI through custom elements, Shadow DOM, templates, and slots. A strong component behaves like a well-designed built-in element.

By the end of this lesson, you'll be able to:

  • Define a custom element
  • Use lifecycle callbacks and Shadow DOM
  • Design accessible attribute/property APIs

Core mental model

Use this decision table as a compact reference. Focus on what each tool means and when it earns its place in production code.

ConceptWhat it meansDecision rule
Custom elementRegistered HTML element classUse for reusable platform-native UI boundaries
Shadow DOMEncapsulated subtree and style scopeUse when encapsulation outweighs global styling needs
SlotConsumer-provided content insertion pointUse to preserve semantic light-DOM content

Professional workflow

Build the behavior in small, observable steps. Each step should leave something you can inspect or test.

  1. Describe the custom-element lifecycle boundary: inputs, outputs, state, timing, and expected failures.
  2. Implement the smallest correct path with names that expose intent.
  3. Add edge cases and failure handling before introducing abstractions.
  4. Verify behavior with realistic data and one deliberately adversarial example.
  5. Refactor only after the observable behavior is protected.

Make behavior observable

Before optimizing or abstracting, make inputs, outputs, state changes, timing, and failure paths visible. JavaScript becomes much easier to reason about when hidden work is exposed.

Guided code lab

Create a resilient status badge

The element reflects an attribute into an accessible internal label.

status-badge.js
class StatusBadge extends HTMLElement {
  static observedAttributes = ["status"];
  connectedCallback() { this.render(); }
  attributeChangedCallback() { this.render(); }
  render() {
    const status = this.getAttribute("status") ?? "unknown";
    this.textContent = status;
    this.setAttribute("role", "status");
  }
}
customElements.define("status-badge", StatusBadge);

Use template and slot

The host provides content; the component supplies layout and a named action area.

course-card.html
<template id="course-card-template">
  <article>
    <h2><slot name="title"></slot></h2>
    <slot></slot>
    <footer><slot name="action"></slot></footer>
  </article>
</template>

Production practice

Contract

Keep the custom-element lifecycle API explicit: accepted state, emitted events, DOM ownership, and cleanup.

Verification

Test with keyboard input, missing elements, repeated initialization, and teardown—not only a mouse happy path.

Operations

Measure user-visible latency and remove listeners, observers, object URLs, or media tracks when the feature ends.

Common failure mode

Hiding native interactive semantics inside a custom element without keyboard, label, form, and focus behavior creates an inaccessible imitation.

Independent workshop

Build a reusable course-progress custom element with a slot-based label and status event.

Your finished workshop must include:

  • Documented attributes and properties
  • Lifecycle-safe listeners
  • Keyboard and screen-reader behavior

Definition of done

Demonstrate the happy path and at least two edge cases, keep responsibilities separated, and add a short note explaining one design choice.

Recap & quick check

Key takeaways

  • Custom elements define reusable tags
  • Lifecycle callbacks own setup and cleanup
  • Shadow DOM encapsulates
  • Slots accept consumer content

Quick check

1. What must custom-element names contain?

2. Where should cleanup happen?

3. What does a slot receive?

Keep the workshop. Later modules deliberately build on these decisions, so each exercise can become part of your final portfolio architecture.