--- name: supporting-custom-elements description: Teaches Web Components (Custom Elements) support in React 19, including property vs attribute handling and custom events. Use when integrating Web Components or working with custom HTML elements. allowed-tools: Read, Write, Edit version: 1.0.0 --- # Web Components Support in React 19 This skill teaches you how to use Web Components (Custom Elements) in React 19, which now has full support. This skill activates when: - Working with Web Components or Custom Elements - Integrating third-party web components - Passing props to custom elements - Handling custom events from web components - Migration from React 18 web component workarounds React 19 adds full support for Custom Elements and passes all Custom Elements Everywhere tests. **New in React 19:** 1. **Automatic Property Detection** - React determines property vs attribute 2. **Custom Event Support** - Standard `on + EventName` convention 3. **Boolean Attributes** - Properly handled (added/removed as needed) 4. **No Ref Workarounds** - Pass props directly to custom elements **Before React 19:** ```javascript { if (el) { el.increment = increment; el.isDark = isDark; } }} /> ``` **React 19:** ```javascript setCount(count + 1)} /> ``` ## Using Web Components **Step 1: Define Custom Element** (or use third-party) ```javascript class WebCounter extends HTMLElement { connectedCallback() { this.render(); } set increment(fn) { this._increment = fn; } set isDark(value) { this._isDark = value; this.render(); } handleClick() { this._increment?.(); this.dispatchEvent(new CustomEvent('incremented')); } render() { this.innerHTML = ` `; this.querySelector('button').onclick = () => this.handleClick(); } } customElements.define('web-counter', WebCounter); ``` **Step 2: Use in React 19** ```javascript function App() { const [count, setCount] = useState(0); const [isDark, setIsDark] = useState(false); const increment = () => setCount(count + 1); return (

Count: {count}

console.log('Incremented!')} />
); } ``` **Step 3: Handle Custom Events** Custom events follow `on + EventName` convention: ```javascript ``` If custom element dispatches `buttonClick` event, React automatically wires it up.
## Example: Third-Party Web Component ```javascript import '@material/mwc-button'; function MaterialButton() { return alert('Clicked!')} />; } ``` ## Example: Custom Form Element ```javascript class RatingInput extends HTMLElement { connectedCallback() { this.rating = 0; this.render(); } setRating(value) { this.rating = value; this.dispatchEvent( new CustomEvent('ratingChange', { detail: { rating: value }, }) ); this.render(); } render() { this.innerHTML = ` ${[1, 2, 3, 4, 5] .map( (i) => ` ` ) .join('')} `; this.querySelectorAll('button').forEach((btn) => { btn.onclick = () => this.setRating(+btn.dataset.rating); }); } } customElements.define('rating-input', RatingInput); ``` ```javascript function ReviewForm() { const [rating, setRating] = useState(0); return (
setRating(e.detail.rating)} />

Rating: {rating}

); } ``` For comprehensive Custom Elements documentation, see: `research/react-19-comprehensive.md` lines 1034-1089.
## MUST - Use standard `on + EventName` for custom events - Let React determine property vs attribute automatically - Define custom elements before using in React ## SHOULD - Prefer Web Components for framework-agnostic widgets - Use TypeScript declarations for custom elements - Test SSR vs CSR rendering differences ## NEVER - Use ref workarounds (React 19 handles props directly) - Forget to define custom elements (will render as unknown tag) - Pass non-primitive values in SSR context (will be omitted)