forwardRef Pattern
forwardRef Pattern
Section titled “forwardRef Pattern”Introduction
Section titled “Introduction”forwardRef lets a parent component pass a ref through to a child component’s underlying DOM node. This is essential for reusable component libraries where the parent needs to focus an input, measure a node, or trigger animations inside a child component.
Basic forwardRef
Section titled “Basic forwardRef”import { forwardRef, useRef } from 'react';
// forwardRef wraps the component to accept a ref from the parentconst FancyInput = forwardRef(function FancyInput(props, ref) { return ( <input ref={ref} {...props} style={{ padding: '10px', border: '2px solid #6366f1', borderRadius: '6px', outline: 'none' }} /> );});
// Parent component uses the refexport default function Form() { const inputRef = useRef(null);
const handleFocus = () => { inputRef.current.focus(); };
return ( <div> <FancyInput ref={inputRef} placeholder="Type here..." /> <button onClick={handleFocus}>Focus Input</button> </div> );}Explanation: forwardRef receives props and ref as separate arguments. It attaches the ref to the <input> element. The parent passes ref={inputRef} and can call .focus() on it.
React 19: Ref as a Prop
Section titled “React 19: Ref as a Prop”In React 19, forwardRef is no longer required. You can pass ref as a regular prop:
// React 19+ — no forwardRef needed!function FancyInput({ ref, ...props }) { return ( <input ref={ref} {...props} style={{ padding: '10px', border: '2px solid #6366f1', borderRadius: '6px' }} /> );}
// Usage is the same<FancyInput ref={inputRef} placeholder="Type here..." />forwardRef still works for backward compatibility, but in React 19, treating ref as a regular prop is the recommended approach.
useImperativeHandle: Custom Ref API
Section titled “useImperativeHandle: Custom Ref API”useImperativeHandle customizes the value exposed to the parent. Instead of exposing the entire DOM node, you expose only specific methods.
import { forwardRef, useRef, useImperativeHandle } from 'react';
const CustomInput = forwardRef(function CustomInput(props, ref) { const inputRef = useRef(null);
// Define exactly what the parent can access useImperativeHandle(ref, () => ({ focus: () => inputRef.current.focus(), clear: () => { inputRef.current.value = ''; }, getValue: () => inputRef.current.value, select: () => inputRef.current.select() }));
return <input ref={inputRef} {...props} />;});
// Parent — can only access the exposed methodsfunction Form() { const inputRef = useRef(null);
const handleClear = () => { inputRef.current.clear(); // ✅ Available // inputRef.current.style = ... // ❌ Not exposed — DOM node hidden };
return ( <div> <CustomInput ref={inputRef} placeholder="Enter text" /> <button onClick={() => inputRef.current.focus()}>Focus</button> <button onClick={handleClear}>Clear</button> </div> );}Performance note: Include a dependency array as the third argument to avoid recreating the imperative handle on every render:
useImperativeHandle(ref, () => ({ focus: () => inputRef.current.focus(), reset: () => resetForm()}), [resetForm]); // Only recreate when resetForm changesCommon Patterns
Section titled “Common Patterns”Forwarding Multiple Refs
Section titled “Forwarding Multiple Refs”For complex components that need multiple internal refs, combine forwardRef with regular refs:
const DateRangePicker = forwardRef(function DateRangePicker(props, ref) { const startRef = useRef(null); const endRef = useRef(null);
useImperativeHandle(ref, () => ({ focus: () => startRef.current.focus(), getRange: () => ({ start: startRef.current.value, end: endRef.current.value }), clear: () => { startRef.current.value = ''; endRef.current.value = ''; } }));
return ( <div> <input ref={startRef} type="date" /> <span> to </span> <input ref={endRef} type="date" /> </div> );});Summary
Section titled “Summary”forwardRefpasses a ref from parent to child’s DOM node.- In React 19,
refis a regular prop —forwardRefis optional. useImperativeHandlelimits what the parent can access via ref.- Use
useImperativeHandlefor form inputs, modals, and reusable library components.