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.
Enables React 19 Web Components integration with automatic property detection and custom event handling. Use when working with custom HTML elements or third-party web components to pass props directly without ref workarounds.
/plugin marketplace add djankies/claude-configs/plugin install react-19@claude-configsThis skill is limited to using the following tools:
New in React 19:
on + EventName conventionBefore 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)} />
</overview>
<workflow>
## Using Web Components
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!')} />;
}
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.
</examples>