skip to content

In React 19, how does a parent component get the DOM node rendered by a child function component, and what changed compared with the forwardRef pattern?

level: middleimportance: should knowfreq 56%

answer

  1. ref used to be stripped from props
  2. now it is just a prop
  3. the child must pass it onto a host element
  4. forwardRef is legacy, not removed
  5. classes still hand back the instance

basics

~20 s

In React 19, ref is an ordinary prop on function components. The child declares ref in its props and spreads or passes it to the DOM element it renders; the parent just writes ref={someRef}. forwardRef is no longer needed for this and exists mainly for older code.

solid answer

~50 s

React 19 made `ref` a regular prop for function components. The child accepts it like any other prop — `function TextInput({ ref, ...rest })` — and forwards it onto the host element it renders, `<input ref={ref} {...rest} />`. The parent writes `<TextInput ref={inputRef} />` and gets the DOM node in `inputRef.current` after commit. Before React 19, `ref` was stripped out of props by the element machinery, so a component had to be wrapped in `forwardRef((props, ref) => …)` to receive it at all. That wrapper still works and is not removed, but it is no longer required for new code, and the React team has signalled it will be deprecated later. Two things did not change: a ref pointed at a class component still gives you the instance rather than a DOM node, and a component that never forwards the ref anywhere still hands the parent nothing.

go deeper

for a junior

Know that in React 19 a child component can accept ref as a normal prop and pass it to the DOM element it renders, and that the parent then reads the node from ref.current after mount.

for a middle

Explain why forwardRef existed — ref used to be stripped out of props — and show the modern equivalent. Be clear that forwarding is opt-in and a child that ignores the prop leaves the parent with null.

for a senior

Show the library-author view: which components in a design system expose their node, what wrappers must forward, and when you narrow the surface with useImperativeHandle instead of handing out the raw element.

for a principal

Own the migration and the policy: how forwardRef gets retired across a large codebase, and what your API contract says about which components publish DOM access at all, since every exposed node is a coupling you cannot refactor away later.

## The problem the pattern solves A parent that wants to focus an input, scroll a container into view, or hand a node to something imperative needs the actual DOM node. But the parent renders `<TextInput />`, a component — and components are not DOM nodes. Something has to carry the parent's ref through the component and down onto the host element that eventually renders. ## How it works in React 19 `ref` is an ordinary prop on function components. Nothing special happens to it on the way in: ```jsx function TextInput({ ref, label, ...rest }) { return ( <label> {label} <input ref={ref} {...rest} /> </label> ); } function Form() { const inputRef = useRef(null); useEffect(() => { inputRef.current.focus(); }, []); return <TextInput ref={inputRef} label="Email" />; } ``` The parent's ref object travels as a prop, the child attaches it to the `<input>`, and React fills `inputRef.current` with that `<input>` node during commit. Because it is a plain prop, it also behaves like one: you can rename it, pass it conditionally, give it a default, or put it in a props object you spread — none of which was possible when it was special-cased. ## What it replaced Before React 19, `ref` (like `key`) was extracted by element creation and never appeared in `props`. A component that wanted one had to be wrapped: ```jsx const TextInput = forwardRef(function TextInput(props, ref) { return <input ref={ref} {...props} />; }); ``` The wrapper's only job was to deliver a second argument. It cost a layer of indirection, confused component display names, complicated typing, and interacted awkwardly with other higher-order wrappers — every wrapper in the chain had to remember to forward the ref or the parent silently got `null`. `forwardRef` still exists in React 19 and existing code keeps working; the React team has said it is on a path to deprecation, and a codemod exists for the migration. Treat it as a reading-and-migration skill: recognise it, know what it did, convert it when you touch the file. ## What did not change **Refs are still opt-in.** A component only exposes a node if it deliberately forwards the ref onto something. `<TextInput ref={inputRef} />` against a child that ignores the prop leaves `inputRef.current` as `null` forever — with no error. This is a feature: a component's DOM is its implementation detail unless it chooses to publish it. **Class components are unchanged.** Passing `ref` to a class component gives you the class *instance*, not a DOM node, and classes do not receive `ref` in `this.props`. The prop-based rule is a function-component rule. **Timing is unchanged.** The parent's ref is populated during commit, so it is `null` while the parent renders and available in the parent's effects and handlers. **You can still narrow what you expose.** If the child does not want to hand out its raw node, it can attach its own internal ref to the DOM element and use `useImperativeHandle(ref, …)` to publish a small object instead. Whether the ref arrived as a prop or through `forwardRef` makes no difference to that. ## Interop gotchas worth knowing - A component that only spreads `{...rest}` onto its host element and never destructures `ref` will *not* forward it in a codebase using the plain-prop form unless `ref` is actually inside that rest object — so be explicit about whether you destructured it out. - Passing a ref to a component that renders a Fragment or nothing has nowhere to land; the ref stays `null`. - Wrapping components (providers, layout helpers, animation wrappers) still have to forward the ref down explicitly. Making `ref` a plain prop made that easier to do, not automatic. ## How to answer this in an interview Lead with the current shape — `ref` is a prop, the child forwards it onto a host element — then show you can read legacy code by explaining what `forwardRef` was for and why it existed. That order matters: `forwardRef` is history, and presenting it as the recommended approach in React 19 is the mistake being tested for.

  • What does the parent see if the child accepts ref but never attaches it to anything?
    `ref.current` stays `null`, silently — React does not warn, because not exposing a node is a legitimate choice. That is the whole point of refs being opt-in: a component's internal DOM is private until it deliberately publishes it, either by forwarding the ref onto a host element or by narrowing it with useImperativeHandle.
  • Does making ref a plain prop change anything about when the parent can read ref.current?
    No. Attachment still happens during commit, so the parent's `ref.current` is `null` while the parent renders and populated by the time the parent's layout effects and effects run. The change is purely about how the ref reaches the element, not about when React fills it in.
  • How is passing a ref to a class component different?
    A ref on a class component gives you the component *instance*, not a DOM node, so you can call its public methods — the mechanism that predates hooks entirely. Classes do not receive `ref` in `this.props`, so the plain-prop rule does not apply to them; to reach a DOM node inside a class you attach a separate ref to the host element.

saying these in an interview costs you the question

  • Says forwardRef is still required in React 19
  • Thinks ref={x} on a component automatically reaches its root DOM node
  • Expects a warning when a child never forwards the ref
  • Claims key also became a normal prop in React 19
  • Says a ref on a class component returns its rendered element

context