Most parent→child communication in React is declarative: the parent passes props down. But sometimes a parent needs to imperatively trigger something on a child — focus an input, open a modal, scroll a list, play a video. useImperativeHandle lets a child expose a small, explicit API on its ref instead of leaking its internals.
The shape
import { forwardRef, useImperativeHandle, useRef } from 'react';
const FancyInput = forwardRef(function FancyInput(props, ref) {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus(),
clear: () => { inputRef.current.value = ''; },
}), []);
return <input ref={inputRef} {...props} />;
});
Now the parent holds a ref and calls those methods — and only those methods:
function Form() {
const inputRef = useRef(null);
return (
<>
<FancyInput ref={inputRef} placeholder="Email" />
<button onClick={() => inputRef.current.focus()}>Focus</button>
<button onClick={() => inputRef.current.clear()}>Clear</button>
</>
);
}
Three pieces working together:
forwardReflets a component receive areffrom its parent (a function component can’t get one otherwise). In React 19+,refcan be a normal prop, but forwardRef is still the widely-deployed pattern.useImperativeHandle(ref, factory, deps)sets whatref.currentbecomes — the object your factory returns — instead of the raw DOM/native node.- The internal
inputRefstays private; the parent can’t reach into it.
useImperativeHandle customizes the value a parent’s ref points to, letting a child publish a deliberate imperative API (e.g. focus(), open()) while hiding its internals.When is this actually the right tool?
Default to declarative — lift state up or pass props. Reach for an imperative handle only for actions that don’t naturally map to state:
- Focus / blur / scroll-to / select text
- Play / pause / seek on a media component
- Open / close / shake an animation on an imperative component (a toast, a bottom sheet)
- Triggering an imperative animation library call
Say it out loud (30s)
“
forwardReflets a child receive a parent’s ref;useImperativeHandlereplaces what that ref points to with a custom object of methods, so the child exposes a deliberate API likefocus()oropen()while keeping its internals private. I use it only for imperative actions that don’t map to state — focus, scroll, play/pause, opening a modal — and otherwise keep things declarative by lifting state up.”