Back to React
React
medium
mid

What does forwardRef do in React, and when would you reach for useImperativeHandle?

`forwardRef` forwards a ref through a component so parents can access the underlying DOM node or imperative API. `useImperativeHandle` customizes what the ref exposes — handy when you need to expose specific methods (`focus`, `scrollIntoView`, `open()`) without leaking the DOM node. In React 19, regular function components accept ref as a prop, removing the need for forwardRef.

3 min read·~8 min to think through

Two related APIs around imperative escape hatches in a mostly-declarative library.

forwardRef

tsx
const Input = forwardRef<HTMLInputElement, Props>((props, ref) => (
  <input ref={ref} {...props} />
));

Without forwardRef, a parent passing ref to <Input> would attach to the component instance (function components have no instance, so it'd be null). forwardRef lets the ref reach the DOM node.

Use it on leaf primitives that wrap a single DOM element — Button, Input, Textarea, MenuItem.

React 19 update

React 19 lets regular function components accept ref as a regular prop:

tsx
function Input({ ref, ...props }: Props & { ref?: Ref<HTMLInputElement> }) {
  return <input ref={ref} {...props} />;
}

forwardRef is being deprecated. New code in React 19+ should use prop-style refs.

useImperativeHandle

When you don't want to expose the DOM node directly but a curated API:

tsx
type DialogHandle = { open: () => void; close: () => void };

const Dialog = forwardRef<DialogHandle, Props>((props, ref) => {
  const [open, setOpen] = useState(false);
  useImperativeHandle(ref, () => ({
    open: () => setOpen(true),
    close: () => setOpen(false),
  }), []);
  return open ? <DialogShell ...> ... </DialogShell> : null;
});

// Parent
const dialogRef = useRef<DialogHandle>(null);
dialogRef.current?.open();

The ref now exposes open() / close() instead of the DOM node — encapsulated imperative API.

When to use which

NeedUse
Pass-through ref to a DOM nodeforwardRef (or ref prop in React 19)
Expose custom methodsuseImperativeHandle
Coordinate work across componentsLift state up; rarely refs

When to avoid

  • Don't reach for imperative APIs to do what state can do. <Dialog open={isOpen}> is cleaner than ref.current.open().
  • Don't use useImperativeHandle to leak internals. If you expose 10 methods, the component is doing too much.

Use cases that legitimately need imperative handles

  • Animations that don't fit declaratively (focus management after a transition).
  • Third-party integration (a Stripe payment form ref that exposes submit()).
  • Library components with measure / scrollIntoView / focus methods.

Gotcha: ref + memo

Until React 19, you needed forwardRef inside memo() in a specific order. In 19, both go away as constraints.

Interview framing

"forwardRef lets a parent ref reach through your component to the underlying DOM node — typically for design-system primitives. useImperativeHandle lets you expose a curated set of methods (focus, open, scrollIntoView) instead of the raw node. Use it when there's a legitimate imperative need — animation, focus orchestration, third-party widget control — but prefer declarative props for state-driven behavior. In React 19, function components accept ref as a regular prop, so forwardRef is being deprecated; useImperativeHandle still works the same."

Follow-up questions

  • When have you used useImperativeHandle in production?
  • How does React 19 change ref forwarding?
  • Why prefer props over refs for state?

Common mistakes

  • Reaching for refs when state would work.
  • Exposing the whole DOM node when only one method is needed.
  • Forgetting forwardRef and silently dropping refs (pre-19).

Performance considerations

  • No measurable cost. Just a discipline issue: imperative escape hatches hide data flow.

Edge cases

  • Strict Mode double-invocation interacting with imperative handles.
  • Cleanup on unmount when handle holds references.
  • TypeScript ref typing — Ref vs RefObject vs MutableRefObject.

Real-world examples

  • Radix primitives, MUI, react-hook-form Controller exposing focus(), video player libs.

Senior engineer discussion

Seniors reach for imperative handles rarely and intentionally. They push back on PRs that use them where props would work — it's almost always a code smell except for the legitimate cases listed.

Related questions