Sometimes a parent needs to call a child imperatively, such as focusing an input or scrolling a list. useImperativeHandle lets the child decide exactly what the ref exposes, so the parent gets a small, intentional API instead of the raw DOM node.
Before you start
You should be comfortable with refs, forwardRef and component boundaries. This article covers imperative handles; it assumes you already avoid refs for ordinary data flow.
Step-by-step walkthrough
Step 1: Forward the ref to the child
Wrap the child in forwardRef so it can receive the ref the parent attaches. The child owns the internal DOM ref and decides what to expose; the parent never reaches into the DOM directly.
Step 2: Define the handle with useImperativeHandle
useImperativeHandle(ref, () => ({ focus: () => inputRef.current?.focus() }), []) publishes only the methods the parent needs. The dependency array controls when the handle is recreated, so keep it minimal to avoid churn and stale closures.
Step 3: Prefer props-first design
Imperative handles are appropriate for focus, scroll, playback and measurement, where there is no declarative equivalent. For data, states and events, pass props or callbacks instead: props keep the data flow one-directional and testable.
Worked scenario
The parent can focus the field without seeing the input element.
import { forwardRef, useImperativeHandle, useRef } from 'react';
const FancyInput = forwardRef(function FancyInput(props, ref) {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
focus: () => inputRef.current?.focus(),
}), []);
return <input ref={inputRef} {...props} />;
});
function Form() {
const ref = useRef(null);
return (
<>
<FancyInput ref={ref} />
<button onClick={() => ref.current?.focus()}>Focus</button>
</>
);
}Walk through the example
FancyInput keeps its input element private and exposes only focus. The parent’s ref resolves to that handle, so ref.current?.focus() calls the method rather than touching the DOM. The child can change its internals without breaking the parent, as long as the handle stays the same.
Common mistake
Exposing the whole DOM node via useImperativeHandle(ref, () => inputRef.current), which lets parents manipulate internals and defeats encapsulation. Another is using a ref for data that should be props, which hides the data flow and causes missed re-renders.
Verify the behavior
Click the parent button and confirm focus moves to the input. Assert the handle object has only the intended methods, not DOM properties. Change the child’s markup and confirm focus still works, proving the parent does not depend on internals.
Interview exercise
When is an imperative handle better than props?
Answer and reasoning
When the operation has no declarative state to represent, such as moving focus, scrolling to a position, starting media or measuring a node. Those actions are commands, not data, and expressing them as props would require awkward state round trips. For anything that is really data or an event, props and callbacks keep the flow explicit and easier to test.
Continue learning
Compare ref usage in Ref versus state and Layout effect measurement. Read the React useImperativeHandle reference and try the React interview questions.