Using in Frameworks
Web components are just DOM elements
Section titled “Web components are just DOM elements”A custom element registered with customElements.define is a standard DOM node. Any framework that outputs HTML can use it. You place it in JSX, a Vue template, or an Angular template the same way you place a <div>.
Because frameworks ultimately produce real DOM nodes, they all interoperate with custom elements at the same level — the browser’s own element API. Properties, attributes, and events work the same way regardless of which framework is rendering the surrounding tree.
React 18 and earlier
Section titled “React 18 and earlier”In React 18 and earlier, custom elements receive string attributes, not rich object props. Pass primitives (strings, numbers, booleans) as attributes. For complex data (arrays, objects) or for listening to custom events you must use a ref callback:
// React 18 — attach ref to wire up eventsimport { useRef, useEffect } from 'react';
function App() { const ref = useRef(null); useEffect(() => { const el = ref.current; el.addEventListener('my-event', (e) => console.log(e.detail)); return () => el.removeEventListener('my-event', () => {}); }, []); return <my-counter ref={ref} label="Score"></my-counter>;}React 19
Section titled “React 19”React 19 ships full custom element support. Properties and event handlers (prefixed with on) now bind directly without a ref workaround:
// React 19 — direct prop and event bindingfunction App() { return ( <my-counter label="Score" count={0} onmy-event={(e) => console.log(e.detail)} /> );}Vue treats custom elements as first-class citizens. Attributes and properties bind with :prop="value" (property) or attr="value" (attribute). Custom events are listened to with @event-name:
<!-- Vue template --><my-counter :label="label" :count="score" @score-changed="handleChange"/>Vue automatically differentiates between a property (set on the DOM node) and an attribute (set via setAttribute) based on whether the key exists on the element’s prototype.
Angular
Section titled “Angular”Angular supports custom elements via the CUSTOM_ELEMENTS_SCHEMA. Add it to your module’s schemas array to suppress unknown-element errors:
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
@NgModule({ declarations: [AppComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA],})export class AppModule {}In templates, use [property] for property binding and (event-name) for custom events:
<!-- Angular template --><my-counter [label]="title" [count]="score" (score-changed)="onScoreChanged($event)"></my-counter>Runnable demo
Section titled “Runnable demo”The demo below shows a vanilla web component (<demo-badge>) rendered in plain HTML — the same element would work identically when dropped into a React, Vue, or Angular tree because it is just a DOM node.