Refs

useImperativeHandle

Customizes the instance value that is exposed to parent components when using ref forwarding. Restricts which methods the parent can call on the ref.

Signature

TypeScript
useImperativeHandle<T>(ref: React.Ref<T>, createHandle: () => T, dependencies?: any[])

Parameters

ParameterTypeDescription
refReact.Ref<T>The ref received from forwardRef or the ref prop (React 19+).
createHandle() => TA function returning an object with the methods and properties to expose to the parent.
dependenciesany[]Optional dependency array. The handle is recreated when dependencies change.

Return Value

undefined — useImperativeHandle does not return a value.

Examples

Custom Input Handle
import { useRef, useImperativeHandle, forwardRef } from 'react';

interface InputHandle {
  focus: () => void;
  clear: () => void;
  getValue: () => string;
}

const FancyInput = forwardRef<InputHandle, { placeholder?: string }>(
  function FancyInput({ placeholder }, ref) {
    const inputRef = useRef<HTMLInputElement>(null);

    useImperativeHandle(ref, () => ({
      focus: () => inputRef.current?.focus(),
      clear: () => { if (inputRef.current) inputRef.current.value = ''; },
      getValue: () => inputRef.current?.value ?? '',
    }));

    return <input ref={inputRef} placeholder={placeholder} />;
  }
);

function Form() {
  const inputRef = useRef<InputHandle>(null);
  return (
    <div>
      <FancyInput ref={inputRef} placeholder="Type here..." />
      <button onClick={() => inputRef.current?.focus()}>Focus</button>
      <button onClick={() => inputRef.current?.clear()}>Clear</button>
    </div>
  );
}
Video Player Controls
import { useRef, useImperativeHandle, forwardRef } from 'react';

interface VideoHandle {
  play: () => void;
  pause: () => void;
  seek: (time: number) => void;
}

const VideoPlayer = forwardRef<VideoHandle, { src: string }>(
  function VideoPlayer({ src }, ref) {
    const videoRef = useRef<HTMLVideoElement>(null);

    useImperativeHandle(ref, () => ({
      play: () => videoRef.current?.play(),
      pause: () => videoRef.current?.pause(),
      seek: (time: number) => {
        if (videoRef.current) videoRef.current.currentTime = time;
      },
    }));

    return <video ref={videoRef} src={src} />;
  }
);
Scrollable List Handle
import { useRef, useImperativeHandle, forwardRef } from 'react';

interface ListHandle {
  scrollToTop: () => void;
  scrollToBottom: () => void;
  scrollToIndex: (index: number) => void;
}

const ScrollableList = forwardRef<ListHandle, { items: string[] }>(
  function ScrollableList({ items }, ref) {
    const containerRef = useRef<HTMLDivElement>(null);

    useImperativeHandle(ref, () => ({
      scrollToTop: () => containerRef.current?.scrollTo({ top: 0, behavior: 'smooth' }),
      scrollToBottom: () => {
        const el = containerRef.current;
        el?.scrollTo({ top: el.scrollHeight, behavior: 'smooth' });
      },
      scrollToIndex: (i: number) => {
        containerRef.current?.children[i]?.scrollIntoView({ behavior: 'smooth' });
      },
    }));

    return (
      <div ref={containerRef} style={{ maxHeight: 300, overflow: 'auto' }}>
        {items.map((item, i) => <div key={i}>{item}</div>)}
      </div>
    );
  }
);

Common Pitfalls

!

Overusing imperative handles — prefer declarative props and state over imperative methods when possible.

!

Exposing the entire DOM node when only a subset of methods is needed, breaking encapsulation.

!

Forgetting to use forwardRef (or the ref prop in React 19+) to pass the ref from parent to child.

!

Not including dependencies when the handle's methods rely on state or props, causing stale closures.

Understanding useImperativeHandle

useImperativeHandle is a specialized hook that lets you customize what a parent component sees when it holds a ref to your component. Instead of exposing the raw DOM node, you define a curated API surface — only the methods and properties the parent actually needs. This promotes better component encapsulation and prevents parents from relying on implementation details.

This hook is typically used alongside forwardRef, which enables a function component to receive a ref from its parent. In React 19+, function components can accept a ref prop directly without forwardRef, but useImperativeHandle remains the mechanism for customizing what that ref exposes. The create handle function should return an object defining the public API.

The most common use cases involve wrapping complex DOM interactions behind a clean interface. For example, a video player component might expose play(), pause(), and seek() methods while hiding the raw video element. A form input might expose focus() and clear() without granting access to modify the DOM directly. This pattern is also valuable for components built with Canvas or WebGL, where the parent needs to call specific drawing or animation methods.

The dependency array controls when the handle object is recreated. If the handle's methods reference state or props, include those as dependencies. An empty dependency array means the handle is created once and its methods close over the initial values — which may cause stale behavior if those values change. When no dependencies are specified, React recreates the handle on every render.

Related Hooks

More Refs Hooks

Explore All React Hooks

Browse our complete reference of 19 React hooks with signatures, examples, pitfalls, and in-depth explanations.