Skip to main content
Featured Article

Framework-Free UI with Web Components

Learn what Web Components are, their advantages, naming rules, and how to build a reusable design system that works seamlessly in React, Vue, Angular, WordPress, and plain HTML.

  • 10 MIN
  • Pankaj Kumar
Updated: coding

Share

  • Whatsapp Icon
  • Twitter Icon
  • Telegram Icon
  • Linkedin Icon
  • Facebook Icon
Framework-Free UI with Web Components
coding 10 min read

Learn what Web Components are, their advantages, naming rules, and how to build a reusable design system that works seamlessly in React, Vue, Angular, WordPress, and plain HTML.

🧩 What Are Web Components?

Web Components are a set of native browser APIs that let you create custom, reusable HTML elements β€” your own tags β€” with encapsulated structure, style, and behavior.

Example:

<user-card name="Alice"></user-card>

it behaves like a real <div> or <button>, but defined entirely by you.

πŸš€ Key Features of Web Components

FeatureDescription
🧱 Custom ElementsCreate your own HTML tags (<my-card>, <todo-list>)
🎭 Shadow DOMEncapsulates styles and DOM β€” no CSS leakage in or out
🧩 HTML TemplatesReuse HTML chunks easily
πŸ”— ES ModulesImport/export Web Components as modern JavaScript modules
♻️ ReusabilityUse the same component anywhere β€” no framework needed
βš™οΈ Lifecycle CallbacksconnectedCallback, disconnectedCallback, etc. for lifecycle control
🌎 Native SupportRuns in all modern browsers (Chrome, Firefox, Edge, Safari)

βš™οΈ Official Naming Rules for Web Components

naming Web Components has a few strict rules defined by the HTML Custom Elements specification to ensure they don’t conflict with native HTML tags.

βœ… 1. Must contain a hyphen (-)

This is mandatory.

  • βœ… my-button

  • βœ… user-card

  • βœ… app-todo-list

  • ❌ button (invalid β€” conflicts with <button>)

  • ❌ todo (invalid β€” no hyphen)

πŸ’‘ The hyphen prevents clashes with existing or future native HTML tags.

βœ… 2. Cannot start with certain reserved prefixes

Avoid starting with:

  • xml-
  • xsl-
  • svg-

These are reserved for system use and XML-based namespaces.

βœ… 3. Must be all lowercase

Custom element names are case-insensitive, so always use lowercase:

  • βœ… user-profile
  • ❌ UserProfile

βœ… 4. Cannot contain spaces or special characters

Only letters, digits, and hyphens are allowed.

  • βœ… app-nav-bar
  • ❌ app_nav_bar
  • ❌ app.nav.bar

βœ… 5. Cannot be a single word

A single word (like <todo>) is invalid β€” it must be compound (e.g., <todo-item>).

βœ… 6. Should be semantically descriptive

Although not required, good names follow a pattern like:

<namespace>-<component>

Examples:

  • app-header
  • shop-cart-item
  • ui-modal
  • user-avatar
  • profile-card

This makes your components more understandable and avoids naming collisions in large projects.

🧩 Example of Correct Usage

class UserCard extends HTMLElement {
  connectedCallback() {
    this.innerHTML = `<div>Hello, ${this.getAttribute('name')}</div>`;
  }
}

customElements.define('user-card', UserCard);

βœ… Valid tag:

<user-card name="Alex"></user-card>

⚠️ Invalid Examples

NameWhy invalid
buttonConflicts with native tag
UserCardMust be lowercase
app_cardUnderscore not allowed
appNeeds a hyphen
xml-widgetReserved prefix

🧠 Bonus Tip:

If you’re publishing components (e.g., via npm), it’s a good practice to namespace them:

  • myapp-button
  • myapp-card
  • myapp-list

βœ… Advantages (Pros)

1. Framework-agnostic

You can use Web Components with any frontend stack β€” Vue, React, Angular, or even plain HTML.

🧠 They’re just native HTML elements, so any framework that renders HTML can use them.

2. Encapsulation

  • CSS and DOM are completely isolated thanks to the Shadow DOM:
  • No global CSS conflicts
  • Safe to use in any environment (e.g., WordPress themes)

3. Reusability

Once built, your component can be dropped into:

  • a static HTML pag
  • a React app
  • a WordPress block
  • a Webflow embed
  • Without rewriting or refactoring.

4. Performance

Because they’re browser-native, they have no framework runtime overhead. The browser handles everything directly β€” lightweight and fast.

5. Future-proof

Web Components are part of the web platform itself β€” not tied to a specific framework version or library lifecycle.

6. Interoperability

They work across frameworks:

  • Vue 3 β†’ <my-widget></my-widget>
  • React β†’ <my-widget />
  • Angular β†’ <my-widget></my-widget>
  • WordPress β†’ <my-widget></my-widget>

⚠️ Disadvantages (Cons)

DrawbackDescription
🧠 Complex State ManagementNo built-in reactive state like React/Vue β€” must handle manually
🧩 Tooling EcosystemFewer tools, plugins, and patterns than big frameworks
🧡 CommunicationData passing (props, events) can be more verbose
🧰 SSR (Server-Side Rendering)Not natively supported β€” frameworks handle SSR better
🧠 Learning CurveUnderstanding Shadow DOM, slots, lifecycle takes time
⚑ Older Browser SupportIE11 doesn’t support it (requires polyfills)

🧭 When to Use Web Components

βœ… Great for:

  • Design systems / UI libraries (e.g., reusable buttons, modals)
  • Embeddable widgets (e.g., chat widgets, payment forms, dashboards)
  • Multi-framework projects (e.g., company uses Angular + Vue + React)
  • CMS integrations (WordPress, Webflow, Wix, etc.)
  • Web apps that need reusable vanilla components

❌ Not ideal for:

  • Apps requiring deep state management (React/Vue handle that better)
  • Complex routing or SSR needs (Next.js / Nuxt handle that)
  • When you’re already committed to a specific framework ecosystem

🌐 Can You Use Web Components With…

Platform / FrameworkSupportNotes
Vanilla HTML/CSS/JSβœ… NativeNo setup β€” just import the component
WordPressβœ… YesUse <script type="module"> in block/theme β€” great for custom elements
Webflowβœ… YesAdd via β€œEmbed” blocks β€” load JS via CDN or custom code
Vue 3βœ… YesWorks as native elements; just ensure props/events are handled properly
React⚠️ PartialWorks, but React treats Web Components differently (props/events may need manual wiring)
Angularβœ… ExcellentBuilt-in support for custom elements (Angular Elements use same spec)

🧠 Example Integration in Each Environment

πŸ”Ή Vanilla HTML

<script type="module" src="./todo-list.js"></script>

πŸ”Ή React

function App() {
  return <todo-list></todo-list>;
}

If you need to pass props/events, use ref and addEventListener

πŸ”Ή Vue 3

<script setup>
import './todo-list.js';
</script>

πŸ”Ή WordPress

  • You can enqueue your component like this in functions.php:
  • wp_enqueue_script('todo-list', get_template_directory_uri() . '/components/todo-list.js', [], null, true);

Then use:

<todo-list></todo-list>

🏁 Summary

CategoryDetails
Core TechCustom Elements, Shadow DOM, Templates, ES Modules
Main BenefitReusable UI across all frameworks
Best ForWidgets, design systems, multi-framework projects
LimitationsLess built-in state/reacΒ­tivity, limited SSR
CompatibilityWorks with Vue, React, Angular, WordPress, Webflow, and vanilla HTML

βš–οΈ Web Components vs React vs Vue (and Angular)

🧩 Category🌐 Web Componentsβš›οΈ React🟒 Vue 3πŸ…°οΈ Angular
Core ConceptNative browser APIs to create custom HTML elementsVirtual DOM with declarative UI via JSXTemplate-based reactive UIFull-featured framework with TypeScript and DI
Language / SyntaxPlain JS, HTML, and CSSJavaScript / TypeScript with JSXHTML templates + reactive composition APITypeScript
Performance🏎️ Native, minimal overhead⚑ Good, but uses virtual DOM diffing⚑ Excellent with optimized reactivity⚑ Great but heavier runtime
Bundle SizeπŸͺΆ Very small (no framework runtime)βš–οΈ Medium (~30–50 KB)βš–οΈ Medium (~30 KB)🧱 Large (~150 KB+)
Learning Curve🟑 Moderate β€” must learn Shadow DOM, lifecycle, eventsπŸ”΄ Moderate-High β€” JSX, hooks, state management🟒 Easy-Moderate β€” templates are intuitiveπŸ”΄ High β€” many concepts (modules, DI, RxJS)
Reactivity / State Management❌ Manual (use JS classes or libraries like Lit, Signals, or RxJS)βœ… Built-in with hooks & stateβœ… Built-in reactivity (Composition API)βœ… Built-in (RxJS, services)
DOM Handlingβœ… Real DOMβš™οΈ Virtual DOMβš™οΈ Virtual DOM + fine-grained reactivityβš™οΈ Virtual DOM
CSS Encapsulationβœ… Native Shadow DOM scoping❌ Scoped via CSS-in-JS or modulesβœ… Scoped styles in SFCsβœ… View encapsulation
Data Bindingβš™οΈ Manual (attributes/events)βœ… One-wayβœ… Two-way (v-model)βœ… Two-way ([(ngModel)])
SSR (Server-Side Rendering)❌ Not natively supportedβœ… Next.js / frameworksβœ… Nuxt.jsβœ… Angular Universal
Tooling / Dev Ecosystem🟑 Limited but growing (Stencil, Lit, Hybrids)🟒 Mature (React DevTools, CRA, Next.js)🟒 Mature (Vue DevTools, Vite, Nuxt)🟒 Mature (CLI, schematics)
Browser Supportβœ… Native in all modern browsers (polyfill for old IE)βœ… All modern browsersβœ… All modern browsersβœ… All modern browsers
Framework IndependenceπŸ₯‡ Yes β€” pure web standard❌ No❌ No❌ No
Integration in CMS (WordPress/Webflow)βœ… Very easy (native HTML tags)⚠️ Needs bundling/transpilation⚠️ Needs build setup⚠️ Needs full app integration
Best Use CaseReusable widgets, design systems, micro frontendsFull SPA, UI with dynamic dataFull SPA or progressive enhancementsEnterprise-scale apps
Community & Ecosystem🟑 Smaller but W3C-standardized🟒 Massive🟒 Large and friendly🟒 Enterprise-focused
Learning Resources🟑 Medium (fewer tutorials)🟒 Huge community🟒 Very large🟒 Extensive docs
Future Stability🟒 High β€” built into browsers🟒 High β€” constantly maintained🟒 High β€” official support🟒 High β€” Google-maintained

🧠 Quick Takeaways

βœ… Use Web Components When:

  • You need reusable, framework-independent UI (e.g., share across React + Vue + Angular apps).
  • You’re building widgets, dashboards, or CMS embeds (like <user-chat> or <price-widget>).
  • You prefer no build step and want to ship pure HTML/JS/CSS.

βš›οΈ Use React When:

  • You want large community support, rich ecosystem, and SSR (Next.js).
  • You need fine-grained control over state and data flow.
  • You’re building a modern SPA or dashboard.

🟒 Use Vue 3 When:

  • You like template-based syntax and reactive state out of the box.
  • You want a balance between simplicity and power.
  • You prefer cleaner code and great DX (Developer Experience).

πŸ…°οΈ Use Angular When:

  • You’re building large, enterprise-scale applications.
  • You need strict typing, dependency injection, and official structure.
  • You prefer a complete solution (routing, forms, HTTP, testing).

🌍 Hybrid Strategy (Best of All Worlds)

  • Many companies now mix Web Components with frameworks:
  • Design Systems: build UI atoms (e.g., <ui-button>, <ui-modal>) as Web Components β†’ use in React/Vue/Angular apps.
  • CMS Widgets: embed reusable Web Components into WordPress or Webflow sites.
  • Micro-frontends: teams use different frameworks but share the same Web Components.

Mini Design System

let’s build a mini design system using Web Components that works everywhere (React, Vue, Angular, WordPress, Webflow, or plain HTML).

We’ll create 3 reusable UI elements:

  • <ui-button> β€” styled button
  • <ui-card> β€” content card
  • <ui-modal> β€” modal dialog

All built with native Web Components, no frameworks or build tools.

🧱 1️⃣ Base Setup β€” ui-button.js

class UIButton extends HTMLElement {
  static get observedAttributes() {
    return ['variant'];
  }

  constructor() {
    super();
    this.attachShadow({ mode: 'open' });

    this.shadowRoot.innerHTML = `
      <style>
        button {
          font-family: system-ui, sans-serif;
          font-size: 1rem;
          padding: 8px 16px;
          border: none;
          border-radius: 8px;
          cursor: pointer;
          transition: background 0.2s ease;
        }

        button.primary {
          background: #007bff;
          color: white;
        }

        button.primary:hover {
          background: #0056b3;
        }

        button.outline {
          border: 2px solid #007bff;
          color: #007bff;
          background: transparent;
        }

        button.outline:hover {
          background: #e8f0ff;
        }
      </style>
      <button class="primary"><slot></slot></button>
    `;
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (name === 'variant') {
      const btn = this.shadowRoot.querySelector('button');
      btn.className = newValue || 'primary';
    }
  }
}

customElements.define('ui-button', UIButton);

βœ… Use it:

<ui-button>Primary</ui-button>
<ui-button variant="outline">Outline</ui-button>

🧩 2️⃣ ui-card.js

class UICard extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.innerHTML = `
      <style>
        :host {
          display: block;
          border-radius: 12px;
          box-shadow: 0 2px 5px rgba(0,0,0,0.1);
          background: white;
          padding: 16px;
          font-family: system-ui, sans-serif;
        }

        ::slotted(h3) {
          margin-top: 0;
        }

        ::slotted(p) {
          color: #555;
        }
      </style>
      <slot></slot>
    `;
  }
}

customElements.define('ui-card', UICard);

βœ… Use it:

<ui-card>
  <h3>Title</h3>
  <p>This is a reusable card.</p>
</ui-card>

πŸͺŸ 3️⃣ ui-modal.js

class UIModal extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });

    this.shadowRoot.innerHTML = `
      <style>
        :host {
          display: none;
          position: fixed;
          inset: 0;
          background: rgba(0, 0, 0, 0.5);
          justify-content: center;
          align-items: center;
        }

        .modal {
          background: white;
          border-radius: 12px;
          padding: 20px;
          max-width: 400px;
          width: 90%;
          box-shadow: 0 2px 10px rgba(0,0,0,0.3);
          animation: fadeIn 0.3s ease;
        }

        @keyframes fadeIn {
          from { transform: translateY(-10px); opacity: 0; }
          to { transform: translateY(0); opacity: 1; }
        }

        button {
          background: #007bff;
          color: white;
          border: none;
          border-radius: 6px;
          padding: 6px 12px;
          cursor: pointer;
        }

        button:hover {
          background: #0056b3;
        }
      </style>
      <div class="modal">
        <slot></slot>
        <div style="text-align:right; margin-top:1em;">
          <button id="closeBtn">Close</button>
        </div>
      </div>
    `;
  }

  connectedCallback() {
    this.shadowRoot.querySelector('#closeBtn').addEventListener('click', () => this.close());
  }

  open() {
    this.style.display = 'flex';
  }

  close() {
    this.style.display = 'none';
  }
}

customElements.define('ui-modal', UIModal);

βœ… Use it:

<ui-button id="openModal">Open Modal</ui-button>
<ui-modal id="myModal">
  <h3>Hello!</h3>
  <p>This is a reusable modal component.</p>
</ui-modal>

<script type="module">
  import './ui-button.js';
  import './ui-modal.js';
  import './ui-card.js';

  const modal = document.getElementById('myModal');
  document.getElementById('openModal').addEventListener('click', () => modal.open());
</script>

βš™οΈ File Structure Example

/components/
  ui-button.js
  ui-card.js
  ui-modal.js
index.html

πŸ’‘ Integration Examples

βœ… Vanilla HTML

<script type="module" src="./components/ui-button.js"></script>
<ui-button>Click Me</ui-button>

βœ… Vue 3

<template>
  <ui-button>Click Me</ui-button>
  <ui-card><h3>Hello Vue</h3><p>Using Web Components!</p></ui-card>
</template>
<script setup>
import '../components/ui-button.js';
import '../components/ui-card.js';
</script>

βœ… React

import '../components/ui-button.js';
import '../components/ui-modal.js';

export default function App() {
  return (
    <>
      <ui-button id="openModal">Open</ui-button>
      <ui-modal id="myModal">
        <h3>Hello React</h3>
      </ui-modal>
    </>
  );
}

For events in React, use ref and addEventListener (React doesn’t natively handle custom events).

🧠 Why This Is Powerful

BenefitDescription
πŸ’Ž ReusableWorks across React, Vue, Angular, Webflow, and WordPress
🧱 EncapsulatedCSS and DOM scoped via Shadow DOM
πŸš€ FastNo framework overhead
🧩 ComposableComponents can use each other easily
🌍 Deploy AnywhereDrop in as simple <script type="module">

Conclusion

  • Web Components are the future of reusable, framework-independent UI.

  • They bring encapsulation, reusability, and interoperability β€” all powered by native browser APIs.

  • Whether you’re building a design system, widget, or micro frontend, Web Components help you write once and use anywhere.

  • ⚑ In short: Write once, use everywhere.

  • Build UI that’s truly universal β€” no frameworks required.

Explore Related Topics

Stay Updated with Our Latest Articles

Subscribe to our newsletter and get exclusive content, tips, and insights delivered directly to your inbox.

We respect your privacy. Unsubscribe at any time.

About the Author

pankaj kumar - Author

pankaj kumar

Blogger

er....@gma....com