Lesson 79 of 80
A native web component isn't a React component, and JSX's normal prop and event wiring doesn't fully reach it — you still need a ref and a couple of manual DOM calls.
13 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
A web component is a custom HTML element, defined once with the platform's own APIs and usable in any framework — or none. React can render one directly in JSX, <emoji-picker />, and it appears in the DOM exactly like any other tag. But a web component isn't a React component, and the two historically don't speak the same language for two specific things: non-string props, and events with custom names.
React decides a tag is a custom element purely by its name containing a hyphen. Once it knows that, it changes how it treats attributes on it — but that still doesn't make an arbitrary object prop, or an arbitrary custom event, wire up automatically the way they would between two React components.
function EmojiPicker({ onPick }) {
const elRef = useRef(null);
useEffect(() => {
const el = elRef.current;
el.recentEmojis = ["👍", "🎉"]; // an object/array prop: set as a real property, not a JSX attribute
const handlePick = (e) => onPick(e.detail.emoji); // a native CustomEvent, not a React synthetic event
el.addEventListener("emoji-pick", handlePick);
return () => el.removeEventListener("emoji-pick", handlePick);
}, [onPick]);
return <emoji-picker ref={elRef} />;
}
recentEmojis={["👍", "🎉"]} written directly as a JSX attribute wouldn't reliably reach the element as a real array — JSX attributes on a plain HTML-shaped tag serialize to strings the way any DOM attribute does. React 19 improved this specifically for custom elements: if the property already exists on the DOM node, React sets it as a real property instead of stringifying it. Even with that improvement, events are a separate problem — there's no onEmojiPick prop that React wires up for you, because React's synthetic event system only knows about the fixed set of native DOM events it delegates (click, change, and so on), not an arbitrary custom event name a third-party element decided to dispatch. So the ref-plus-useEffect pattern above — read the node, set properties on it, add and remove a listener by hand — is still the real mechanism, React 19 or not.
el.recentEmojis only works once the browser has upgraded that DOM node into the actual custom element class — usually true by the time a useEffect runs, but a real gotcha if the element's definition loads asynchronously after first paint.removeEventListener cleanup leaks a listener on every remount, same as any other manual DOM listener attached via a ref — this isn't unique to web components, but it's easy to forget here because the rest of the wiring already feels like an exception to normal React code.This is the same imperative-ref pattern this track's ref-callbacks lesson uses to react to a DOM node's actual attach and detach — here it's not measuring a node, but manually bridging a native event system that this track's synthetic-events lesson explains React deliberately doesn't extend to arbitrary custom events.