useImperativeHandle
Customise what a parent sees when it holds a ref to your component. Instead of exposing the raw DOM node, you expose a controlled API, only the methods you choose.
01 — What it is
By default, ref on a function component does nothing — function components don't have instances. You need forwardRef to accept a ref, and useImperativeHandle to control exactly what that ref exposes.
This is the escape hatch for imperative behaviour: playing/pausing media, triggering animations, focusing inputs, or toggling internal state from outside the component, without lifting state up.
useImperativeHandle(ref, () => ({
// only expose what you want the parent to call
focus() { inputRef.current.focus(); },
reset() { setValue(""); },
}), [deps]); // optional deps arrayforwardRef. Without it the ref prop is silently discarded and useImperativeHandle has nothing to attach to.02 — How to use it
Wrap the component in forwardRef, define the handle interface, and expose only the methods the parent needs.
// 1. Define what the parent can call
export interface ButtonHandle {
alterToggle: () => void;
}
// 2. Wrap with forwardRef
const Button = forwardRef<ButtonHandle>((_, ref) => {
const [toggle, setToggle] = useState(false);
// 3. Expose a controlled API
useImperativeHandle(ref, () => ({
alterToggle() {
setToggle((prev) => !prev);
},
}));
return <button onClick={() => setToggle(p => !p)}>Child button</button>;
});The parent creates a ref typed to the handle interface and calls methods on it directly — no props, no callbacks, no lifted state.
const buttonRef = useRef<ButtonHandle>(null);
// Call the child's method from the parent
<button onClick={() => buttonRef.current?.alterToggle()}>
Trigger from parent
</button>
// Render the child with the ref attached
<Button ref={buttonRef} />03 — Live demo
Click "Trigger from parent" — it calls alterToggle() on the child component through the ref. The child also has its own button that does the same thing internally.
Child component
The parent calls alterToggle() on the child via ref — without prop drilling
04 — When to use it
| Good use case | Better alternative |
|---|---|
| Focus / scroll / play from parent | — this is exactly what it's for |
| Toggle internal UI state from outside | — fine if lifting state is awkward |
| Sharing business logic upward | Move logic to parent or custom hook |
| Replacing all prop passing | Use props — this hook is an escape hatch |
useImperativeHandle sparingly. Most interactions are better expressed with props and callbacks. It shines for DOM-level control and when you genuinely need to trigger behaviour in a child from an unrelated parent.