4.6 KiB
4.6 KiB
name, description, allowed-tools, version
| name | description | allowed-tools | version |
|---|---|---|---|
| supporting-custom-elements | 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. | Read, Write, Edit | 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
New in React 19:
- Automatic Property Detection - React determines property vs attribute
- Custom Event Support - Standard
on + EventNameconvention - Boolean Attributes - Properly handled (added/removed as needed)
- No Ref Workarounds - Pass props directly to custom elements
Before React 19:
<web-counter
ref={(el) => {
if (el) {
el.increment = increment;
el.isDark = isDark;
}
}}
/>
React 19:
<web-counter increment={increment} isDark={isDark} onIncrementEvent={() => setCount(count + 1)} />
Step 1: Define Custom Element (or use third-party)
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 = `
<button style="color: ${this._isDark ? 'white' : 'black'}">
Increment
</button>
`;
this.querySelector('button').onclick = () => this.handleClick();
}
}
customElements.define('web-counter', WebCounter);
Step 2: Use in React 19
function App() {
const [count, setCount] = useState(0);
const [isDark, setIsDark] = useState(false);
const increment = () => setCount(count + 1);
return (
<div>
<p>Count: {count}</p>
<web-counter
increment={increment}
isDark={isDark}
onIncremented={() => console.log('Incremented!')}
/>
<button onClick={() => setIsDark(!isDark)}>Toggle Theme</button>
</div>
);
}
Step 3: Handle Custom Events
Custom events follow on + EventName convention:
<my-button label="Click Me" onButtonClick={handleClick} />
If custom element dispatches buttonClick event, React automatically wires it up.
import '@material/mwc-button';
function MaterialButton() {
return <mwc-button raised label="Click me" icon="code" onClick={() => alert('Clicked!')} />;
}
Example: Custom Form Element
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) => `
<button data-rating="${i}">⭐</button>
`
)
.join('')}
`;
this.querySelectorAll('button').forEach((btn) => {
btn.onclick = () => this.setRating(+btn.dataset.rating);
});
}
}
customElements.define('rating-input', RatingInput);
function ReviewForm() {
const [rating, setRating] = useState(0);
return (
<form>
<rating-input onRatingChange={(e) => setRating(e.detail.rating)} />
<p>Rating: {rating}</p>
</form>
);
}
For comprehensive Custom Elements documentation, see: research/react-19-comprehensive.md lines 1034-1089.
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)