forwardRef & useImperativeHandle

forwardRef is a React technique that lets a parent pass a ref through a custom component down to a real DOM node inside it, and useImperativeHandle lets that component expose a small set of imperative methods — like focus() or scrollTo() — instead of the raw node. Together they give a parent controlled, imperative access to a child.

Learn forwardRef & useImperativeHandle in our free React course — an interactive lesson with runnable examples, a practice exercise and a quick recall.

Part of the free React course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

1️⃣ Refs Hold a Node You Can Command

A ref created with useRef(null) is just an object with a mutable .current . Attach it to a DOM element and React fills .current with that node, letting you call imperative methods like focus() . The catch: pass that ref to your own component and it doesn't reach the inner node automatically.

2️⃣ forwardRef — Send the Ref Down

Wrap your component in forwardRef so it receives a second argument, ref , after props. Attach that ref to the inner element you want the parent to control. Now {' '} gives the parent the underlying .

3️⃣ useImperativeHandle — Expose a Small API

Often you don't want to hand the parent the raw node — you want a deliberate, minimal API. Inside the forwarded component, call useImperativeHandle(ref, () => ({' '})) to define exactly which methods ref.current exposes. The parent sees only focus() and clear() , not the whole DOM node.

These lines build a forwarded input that exposes a custom API. Put them in the right order:

📋 Quick Reference

1. Why doesn't a ref reach your custom component's input by default?

ref is special and isn't forwarded like other props. Wrap with forwardRef (React 18-) so it reaches the inner node.

Exactly which methods ref.current exposes, instead of the raw DOM node — a small, intentional API.

For imperative actions like focus, scroll, select, play, or measuring the DOM — things props can't naturally express.

Build the handle a component would attach to a forwarded ref — just focus and scrollToTop — then call both. Run it and check your output.

Practice quiz

What does a ref's .current hold after you attach it to a DOM element?

  • The element's value as a string
  • A copy of the component
  • That DOM node, so you can call imperative methods on it
  • null forever

Answer: That DOM node, so you can call imperative methods on it. React fills .current with the DOM node, letting you call methods like focus() on it.

Why doesn't passing a ref to your own component reach its inner node by default (React 18)?

  • ref is a special prop React handles itself, so it isn't forwarded like other props
  • Refs are disabled in custom components
  • The node has no ref slot
  • You must use useState instead

Answer: ref is a special prop React handles itself, so it isn't forwarded like other props. ref is special and not passed through like ordinary props; you wrap with forwardRef to route it.

What does forwardRef let a component do?

  • Skip rendering
  • Share state with its parent
  • Memoize its output
  • Receive a ref as a second argument and attach it to a chosen inner element

Answer: Receive a ref as a second argument and attach it to a chosen inner element. forwardRef gives the component (props, ref); attach ref to the inner node the parent should control.

What does useImperativeHandle do?

  • Forwards all props automatically
  • Lets the component expose a custom set of methods on the parent's ref instead of the raw node
  • Creates a new ref
  • Replaces useState

Answer: Lets the component expose a custom set of methods on the parent's ref instead of the raw node. It defines exactly which methods ref.current exposes (e.g. focus, clear), hiding the raw DOM node.

When is a ref the right choice over a prop?

  • For imperative actions like focus, scroll, select, play, or measuring the DOM
  • For passing data down
  • For conditional rendering
  • For styling

Answer: For imperative actions like focus, scroll, select, play, or measuring the DOM. Refs suit imperative actions props can't express. If a prop can express it, prefer the prop.

How did React 19 change forwardRef usage?

  • It removed refs entirely
  • It made forwardRef mandatory
  • A function component can receive ref as a normal prop, so many cases no longer need forwardRef
  • It renamed it to passRef

Answer: A function component can receive ref as a normal prop, so many cases no longer need forwardRef. In React 19, ref can be a normal prop on function components; useImperativeHandle still exists.

When should you read ref.current?

  • During the render body
  • Inside effects or event handlers, after it's set
  • Before the component mounts
  • In module scope

Answer: Inside effects or event handlers, after it's set. ref.current isn't set during render; read it in effects or event handlers where the node exists.

What's the benefit of exposing a small API with useImperativeHandle rather than the raw node?

  • It is faster
  • It removes the need for forwardRef
  • It avoids re-renders
  • Parents can only call the intended methods, not poke at internals

Answer: Parents can only call the intended methods, not poke at internals. A deliberate, minimal API (e.g. just focus/clear) keeps parents from reaching into internal DOM details.

Inside a forwardRef component, where do you usually attach a useRef for the real element?

  • To the forwarded ref directly
  • To an inner ref, then expose methods via useImperativeHandle on the forwarded ref
  • To the parent's state
  • You don't need an inner ref

Answer: To an inner ref, then expose methods via useImperativeHandle on the forwarded ref. Keep an inner ref on the real <input>, and expose only chosen methods through the forwarded ref.

Your custom component's ref.current is null. What is the common cause in React 18?

  • You forgot useState
  • The ref was created with useMemo
  • You passed a ref to a custom component without wrapping it in forwardRef
  • You called focus() too early

Answer: You passed a ref to a custom component without wrapping it in forwardRef. Without forwardRef, the ref never reaches a real node, so .current stays null.