DevAcademy
LearnReactuseImperativeHandle
IntermediateReact

useImperativeHandle

Learn how to customize the instance value a parent receives when it attaches a ref to your component.

Reading Time

14 min

Lesson

Lesson 21 of 42

The Problem: Exposing Only What’s Needed

By default, attaching a ref to a custom component doesn’t work at all — refs only attach to DOM elements unless the component explicitly forwards one. Even when forwarded, giving the parent direct access to the entire underlying DOM node exposes far more than it usually needs.

What useImperativeHandle Does

useImperativeHandle lets a component define exactly what value should be exposed when a parent attaches a ref to it — typically a small object with only a couple of specific methods, instead of the whole raw DOM node.

Exposing a Limited API from an Input

import { useRef, useImperativeHandle } from "react";

function CustomInput({ ref, ...props }) {
  const inputRef = useRef(null);

  useImperativeHandle(ref, () => ({
    focus() {
      inputRef.current.focus();
    },
    clear() {
      inputRef.current.value = "";
    },
  }));

  return <input ref={inputRef} {...props} />;
}

function Form() {
  const inputRef = useRef(null);

  return (
    <>
      <CustomInput ref={inputRef} />
      <button onClick={() => inputRef.current.focus()}>Focus</button>
      <button onClick={() => inputRef.current.clear()}>Clear</button>
    </>
  );
}

Why Not Just Expose the Whole DOM Node?

Exposing the raw input element would let a parent do anything to it directly — change its style, remove it, read arbitrary properties — tightly coupling the parent to the component’s internal implementation. Exposing only focus() and clear() keeps a clean, intentional boundary between the two.

Older React Versions Needed forwardRef

Before React 19, a component had to be wrapped in forwardRef() to accept a ref prop at all. As of React 19, function components can accept ref directly as a regular prop, removing the need for forwardRef in most cases — useImperativeHandle still works the same way regardless.

Use Sparingly

Reaching for imperative APIs (focus(), clear(), scrollIntoView()) works against React’s declarative model. It’s appropriate for genuinely imperative browser behaviors, but state and rendering should still flow through props and state wherever possible.

Best Practice

Only expose the smallest possible imperative API a parent actually needs — a couple of named methods — rather than the whole underlying DOM node or component instance.

Interview Questions

Quick Quiz

1. What does useImperativeHandle let a component do?

2. Why is exposing a small custom API often better than exposing the raw DOM node?

3. As of React 19, is forwardRef still required to accept a ref prop?