← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

Web Components

JavaScript ES2018 Advanced
debt(d7/e5/b5/t5)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). The detection_hints indicate automated detection is 'no' and tools listed are chrome-devtools and lighthouse — both require manual inspection. Misuses like missing observedAttributes, DOM manipulation in constructor, or framework-specific components where vanilla custom elements would suffice are not caught by linters or compilers; they surface only during testing or code review.

e5 Effort Remediation debt — work required to fix once spotted

Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix describes registering a custom element, but the common_mistakes reveal multiple structural issues — super() placement, lifecycle callback misuse, missing observedAttributes, and shadow DOM encapsulation decisions — that collectively require refactoring component class definitions and potentially revisiting design system architecture across multiple component files.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5). Web Components apply only to the web context but span the entire frontend. A design system built on them imposes ongoing learning costs around lifecycle callbacks, shadow DOM boundaries, and CSS customisation patterns. Every maintainer must understand the nuances (connectedCallback vs constructor, CSS custom properties for theming), creating a persistent productivity tax across all frontend work streams.

t5 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'notable trap (a documented gotcha most devs eventually learn)' (t5). The canonical misconception is that Web Components require no JavaScript, when in fact Custom Elements always need a JS class. Additional traps include DOM manipulation in the constructor (should be in connectedCallback) and missing observedAttributes causing attributeChangedCallback to silently never fire — all documented gotchas that competent developers frequently encounter before learning the correct patterns.

About DEBT scoring →

Also Known As

custom elements shadow DOM HTML templates autonomous custom elements

TL;DR

Browser-native custom elements, shadow DOM, and HTML templates — framework-agnostic reusable components that work in React, Vue, plain HTML, and anywhere.

Explanation

Web Components three specs: Custom Elements (define <my-button> as a class extending HTMLElement), Shadow DOM (encapsulated DOM — styles and selectors don't leak in or out), HTML Templates (<template> not rendered until cloned). Custom element lifecycle: connectedCallback (added to DOM), disconnectedCallback (removed), attributeChangedCallback (observed attribute changed). Slots for content projection. Use cases: design systems that work in any framework, micro-frontend boundaries, widgets embedded in third-party pages.

Common Misconception

Web Components require no JavaScript — Custom Elements require a JavaScript class definition; only HTML Templates can be declared without JS, but still need JS to instantiate and use.

Why It Matters

A design system built with Web Components works in React, Vue, Angular, and plain HTML — truly framework-agnostic components that survive technology changes.

Common Mistakes

  • Shadow DOM when CSS customisation is needed — use CSS custom properties for theming instead
  • Not calling super() first in the constructor — required for custom elements
  • DOM manipulation in the constructor — use connectedCallback instead
  • Not declaring static observedAttributes — attributeChangedCallback never fires without it

Code Examples

✗ Vulnerable
// React-only component — unusable outside React ecosystem:
const Button = ({ label, onClick }) => (
    <button className="btn-primary" onClick={onClick}>{label}</button>
);
// Cannot use in Vue, Angular, plain HTML, or future frameworks
✓ Fixed
// Web Component — works everywhere:
class PrimaryButton extends HTMLElement {
    static observedAttributes = ['disabled'];

    connectedCallback() {
        this.attachShadow({ mode: 'open' });
        this.shadowRoot.innerHTML = `
            <style>button { background: var(--btn-bg, blue); }</style>
            <button part="button"><slot></slot></button>
        `;
    }

    attributeChangedCallback(name, _, value) {
        if (name === 'disabled')
            this.shadowRoot.querySelector('button').disabled = !!value;
    }
}
customElements.define('primary-button', PrimaryButton);
// <primary-button>Click me</primary-button> — works everywhere

Added 16 Mar 2026
Edited 22 Mar 2026
Views 46
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 1 ping W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 1 ping T 0 pings W 0 pings T 2 pings F 1 ping S 1 ping S 1 ping M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 1 ping M 0 pings T 1 ping W 0 pings T 0 pings F 0 pings S 1 ping S 1 ping M 0 pings T 0 pings W
No pings yet today
No pings yesterday
Amazonbot 9 Google 6 Ahrefs 4 Scrapy 4 Perplexity 3 Majestic 2 SEMrush 2 Claude 2 ChatGPT 1 Meta AI 1 Bing 1 PetalBot 1
crawler 33 crawler_json 3
DEV INTEL Tools & Severity
🔵 Info ⚙ Fix effort: High
⚡ Quick Fix
Define your custom element in a JS file, register with customElements.define(), and PHP renders <my-card> as plain HTML — the browser upgrades it when JS loads, giving progressive enhancement for free
📦 Applies To
javascript ES2018 web
🔗 Prerequisites
🔍 Detection Hints
Framework-specific components tied to React/Vue when vanilla custom elements would work across frameworks; no shadow DOM for style encapsulation
Auto-detectable: ✗ No chrome-devtools lighthouse
⚠ Related Problems
🤖 AI Agent
Confidence: Low False Positives: High ✗ Manual fix Fix: High Context: File Tests: Update


✓ schema.org compliant