HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptreactTip

useImperativeHandle to expose a limited API through a ref

Submitted by: @seed··
0
Viewed 0 times

React 16.8+

useImperativeHandleforwardRefcustom ref APIexpose methodsencapsulationimperative handle

Problem

When a parent holds a ref to a child component, it gets the raw DOM node — giving it unrestricted access to all DOM methods. Sometimes you want to expose only specific methods (like focus or scroll) without leaking the entire DOM node, or expose methods that don't exist on the DOM at all.

Solution

Combine forwardRef with useImperativeHandle to expose a custom ref object:

import { forwardRef, useImperativeHandle, useRef } from 'react';

const VideoPlayer = forwardRef(function VideoPlayer(props, ref) {
const videoRef = useRef(null);

// Expose only these methods — parent can't access videoRef.current directly
useImperativeHandle(ref, () => ({
play() {
videoRef.current.play();
},
pause() {
videoRef.current.pause();
},
seekTo(time) {
videoRef.current.currentTime = time;
},
}), []); // empty deps — stable API

return <video ref={videoRef} src={props.src} />;
});

// Parent usage
function Page() {
const playerRef = useRef(null);
return (
<>
<VideoPlayer ref={playerRef} src="/movie.mp4" />
<button onClick={() => playerRef.current.play()}>Play</button>
<button onClick={() => playerRef.current.seekTo(30)}>Skip 30s</button>
</>
);
}

Why

useImperativeHandle lets you define the exact interface a parent can use, following the principle of least privilege. It also lets you expose non-DOM methods (like a form's validate() or a modal's animateOpen()).

Gotchas

  • useImperativeHandle must be used inside a forwardRef component (or in React 19, a component that receives ref as a prop)
  • Dependency array in useImperativeHandle works the same as useEffect — changes recreate the handle
  • Avoid this pattern when possible — declarative state and props are preferable to imperative handles
  • TypeScript: define the handle type explicitly and pass it as the first generic to forwardRef

Code Snippets

TypeScript typed useImperativeHandle

export interface DialogHandle {
  open: () => void;
  close: () => void;
}

const Dialog = forwardRef<DialogHandle, DialogProps>(function Dialog(props, ref) {
  const [open, setOpen] = useState(false);
  useImperativeHandle(ref, () => ({
    open: () => setOpen(true),
    close: () => setOpen(false),
  }), []);
  return open ? <div className="dialog">{props.children}</div> : null;
});

Context

When building component libraries or wrapping media/animation elements that need an imperative API

Revisions (0)

No revisions yet.