Forwarding Refs
forwardRef and ref props let parents access a child’s DOM node: input focus, measuring, and the ref-as-prop modern pattern.
- react
- forwarding-refs
Parents sometimes need a handle to a child’s DOM node: focus an input, measure width, scroll into view, or integrate a non-React library. Refs do that without forcing the child to expose a bulky imperative API.
In current React, function components can accept ref as a normal prop (React 19). Older codebases use forwardRef. Both exist in the wild — know both.
Docs: Referencing Values with Refs, forwardRef.
React 19 style: ref as prop
function TextField({ label, ref, ...props }) {
return (
<label>
{label}
<input ref={ref} {...props} />
</label>
);
}
function Form() {
const inputRef = useRef(null);
return (
<>
<TextField label="Email" ref={inputRef} type="email" />
<button type="button" onClick={() => inputRef.current?.focus()}>
Focus email
</button>
</>
);
}
Classic forwardRef
const TextField = forwardRef(function TextField({ label, ...props }, ref) {
return (
<label>
{label}
<input ref={ref} {...props} />
</label>
);
});
Without forwarding, ref on a custom component does not attach to an inner DOM node (and in older React, was not a regular prop).
Callback refs and lists
const map = useRef(new Map());
items.map((item) => (
<li
key={item.id}
ref={(node) => {
if (node) map.current.set(item.id, node);
else map.current.delete(item.id);
}}
>
{item.label}
</li>
));
Do not overuse
Prefer declarative props when possible (autoFocus, controlled value, CSS). Refs are escape hatches for:
- focus management
- measuring layout (useLayoutEffect)
- third-party widgets
- media playback imperative APIs
Exposing a whole grab-bag of imperative methods belongs in useImperativeHandle with a narrow surface.
Interview out-loud
“Refs hold mutable values that do not trigger re-render. To let a parent reach a child’s DOM node I forward the ref to an inner input or div — via ref-as-prop in React 19 or forwardRef earlier. I keep imperative surfaces small and prefer props for ordinary data flow.”
Related on this site
Further reading
Production checklist
- Source of truth clear for every piece of UI state?
- Remount, route change, and Strict Mode cleanup paths handled?
- Urgent updates separated from deferrable work?
- Profiled before memo, virtualization, or context splits?
- Keyboard, focus, and accessible names still work after the change?
Edge cases worth rehearsing
Interviewers and production incidents cluster around the same edges: first render versus update, empty and loading states, Strict Mode double setup, concurrent interruptions, and what happens when identity (key, route params, user id) changes mid-edit. Walk one concrete user journey end-to-end — open, edit, navigate away, come back — and say which state survives.
Prefer fixing data flow and ownership before reaching for memoization or micro-optimizations. Prefer event handlers over effects when a user action is the trigger. Prefer deriving values during render over mirroring props into state. Prefer stable list keys from business ids. Measure with the profiler when performance is the claim.
When you cite an API, mention one failure mode: abort on unmount, serializable props across server/client boundaries, focus restoration for dialogs, or cache invalidation after a mutation. Specific beats generic every time.
Edge cases worth rehearsing
Interviewers and production incidents cluster around the same edges: first render versus update, empty and loading states, Strict Mode double setup, concurrent interruptions, and what happens when identity (key, route params, user id) changes mid-edit. Walk one concrete user journey end-to-end — open, edit, navigate away, come back — and say which state survives.
Prefer fixing data flow and ownership before reaching for memoization or micro-optimizations. Prefer event handlers over effects when a user action is the trigger. Prefer deriving values during render over mirroring props into state. Prefer stable list keys from business ids. Measure with the profiler when performance is the claim.
When you cite an API, mention one failure mode: abort on unmount, serializable props across server/client boundaries, focus restoration for dialogs, or cache invalidation after a mutation. Specific beats generic every time.
Related guides
- Accessibility Patterns in ReactPractical React a11y: labels, focus management, keyboard, live regions, and composition patterns that stay accessible.
- Avoid Prop Drilling with CompositionStop threading props through intermediates: children slots, inversion of control, and when context is the right escape hatch.
- Batching State UpdatesHow React 18+ batches setState in events, timeouts, and promises: when updates flush and why double setState still works.
- Children Prop PatternsUsing children and slot props for flexible APIs: wrappers, compound components, and when to prefer explicit props.
- Client Component BoundariesWhere to put use client: push interactivity to leaves, serializable props, and children as server slots.