Explorer
React

useImperativeHandle & forwardRef

Understanding useImperativeHandle in React 19

React normally uses props to pass data and callbacks to report interactions. Sometimes a parent needs to perform an action such as focusing an input, selecting text, or scrolling to a section.

Imagine a product dashboard with a search field inside a reusable SearchInput. A button in the parent says “Focus search.” The parent needs to activate that field, while the child owns the input and its markup. A small public method gives them a clear way to cooperate.

useImperativeHandle lets a child customize the value exposed through its ref. That value is called a handle. Usually, it is an object containing a few methods.

The parent requests an action. The child decides how to perform it.

React 19: receive ref directly

Starting with React 19, function components can receive ref as a prop:

JAVASCRIPT
function SearchInput({ ref, placeholder }) {
  // Use the received ref here.
}

Legacy React 18 and earlier used forwardRef((props, ref) => ...) to receive the parent's ref. You do not need that wrapper for this React 19 example.

Receiving a ref does not automatically expose the child's DOM. The child must use it, either on a DOM element or through useImperativeHandle.

Complete example

JAVASCRIPT
import { useImperativeHandle, useRef } from "react";

function SearchInput({ ref }) {
  const inputRef = useRef(null);

  useImperativeHandle(ref, () => ({
    focusInput() {
      inputRef.current?.focus();
    },
    selectInput() {
      inputRef.current?.select();
    },
  }), []);

  return (
    <label>
      Search products
      <input ref={inputRef} type="text" placeholder="Enter a product name" />
    </label>
  );
}

export default function App() {
  const searchRef = useRef(null);

  return (
    <>
      <SearchInput ref={searchRef} />
      <button type="button" => searchRef.current?.focusInput()}>
        Focus search
      </button>
      <button type="button" => searchRef.current?.selectInput()}>
        Select text
      </button>
    </>
  );
}

The parent calls an exposed method. The child implements it using its internal DOM ref. The hook creates the handle; it does not focus the input merely because the component renders.

Why two refs?

Ref

Owned by

What .current holds

searchRef

Parent

The custom object containing focusInput and selectInput

inputRef

Child

The actual input DOM element

The child's ref prop receives the parent's searchRef object; it is not a third ref created by the child.

When the button is clicked:

  1. The parent calls searchRef.current.focusInput().

  2. The exposed method reads the child's inputRef.current.

  3. It calls the browser's focus() method.

  4. The browser focuses the input.

The parent depends on the public method name. The child can change its internal markup while preserving that API.

Two refs are common when hiding a DOM element, but not required by the hook. A handle can expose actions implemented with state setters instead.

Direct DOM exposure versus a custom handle

Without a custom handle, a child can expose its DOM node:

JAVASCRIPT
function SearchInput({ ref }) {
  return <input ref={ref} />;
}

The parent can then call searchRef.current?.focus(), but it also receives the rest of the DOM API. With the custom handle, it receives only the methods you expose. This is an API design boundary, not a security boundary.

Syntax and dependencies

JAVASCRIPT
useImperativeHandle(ref, createHandle, dependencies);

Argument

Purpose

ref

The received ref to which React exposes the handle

createHandle

A function returning the public handle

dependencies

Reactive values used when creating the handle and its methods

The hook returns undefined; the parent accesses the handle through its ref.

An omitted dependency list recreates the handle on each render. With a list, React compares dependencies using Object.is and recreates the handle when they change. An empty list is appropriate in the focus example because its methods use only the stable internal ref object. They read .current when called.

If an exposed method uses a prop, include it:

JAVASCRIPT
function ResetButton({ ref, onReset }) {
  useImperativeHandle(ref, () => ({
    reset() {
      onReset();
    },
  }), [onReset]);

  return <button type="button"
}

Using [] here could retain an old onReset. The same principle applies to state and other reactive values. Do not use an empty list simply to keep the handle stable.

Think of each exposed method as a function that remembers values from the render that created it. The dependency list tells React when that public handle needs refreshing.

Props or an imperative handle?

Props describe what the UI should be. Imperative methods request a particular action.

Requirement

Usual choice

Input value

value and onChange props

Modal visibility

isOpen and onClose props

Selected product

Props/state

Focus or select text

A DOM ref or custom handle

Scroll to an internal section

A custom handle when encapsulation helps

Trigger an animation or external widget action

A handle when props do not naturally express the request

For a modal, prefer <Modal isOpen={isOpen} /> over exposing open() and close() as the default design. An action being possible through a ref does not make it the best interface.

Refs, state, and timing

Changing .current does not trigger a render. A method exposed through a handle can call a state setter, which requests an update normally. The hook does not replace state.

Call these methods from event handlers or appropriate Effects, not during rendering. Refs are initially null and may be unavailable after unmounting or when a child is conditionally absent. Optional chaining avoids calling a method when the handle is missing; it does not queue the action for later.

For controlled inputs, clear or reset through the state setter or owner callback. Directly assigning inputRef.current.value = "" does not update React's controlling state.

Interview takeaway

useRef creates a persistent ref object. React 19 allows a function component to receive ref as a prop. useImperativeHandle defines the public handle exposed through that ref.

One-line definition: useImperativeHandle lets a component customize the methods or values exposed through its ref, so a parent can request imperative actions through a small public API.

For the dashboard example, remember three responsibilities: the parent requests focus, the child implements focus, and the browser performs focus.

Finished this lesson?

Mark this chapter complete to update your learning streak and unlock the next lesson.