Sometimes, a parent component needs to trigger a specific action inside a child component, such as focusing an input or scrolling a container.
React provides useImperativeHandle to expose a controlled imperative API instead of exposing the underlying DOM element directly.
In React 19, function components can receive ref as a prop without using forwardRef.
import {useImperativeHandle,useRef,type ComponentPropsWithRef,type Ref,} from 'react'type InputAPI = {focusInput: () => void}type MyInputProps = Omit<ComponentPropsWithRef<'input'>,'ref'> & {ref?: Ref<InputAPI>}function MyInput({ ref, ...props }: MyInputProps) {const inputRef = useRef<HTMLInputElement>(null)useImperativeHandle(ref,() => ({focusInput() {inputRef.current?.focus()},}),[],)return <input {...props} ref={inputRef} />}
The parent receives only the focusInput() method, not direct access to the child’s internal input ref.
function App() {const myInputRef = useRef<InputAPI>(null)return (<><MyInputref={myInputRef}placeholder="Enter your name"/><button onClick={() => myInputRef.current?.focusInput()}>Focus input</button></>)}
Clicking the button calls the child component’s exposed method, which focuses the actual input element.
The dependency array determines when React recreates the exposed handle.
useImperativeHandle(ref,() => ({focusInput: () => inputRef.current?.focus(),}),[],)
Why is an empty array appropriate here?
inputRef is created by useRef, so its identity is stable.inputRef.current when called, rather than capturing the current DOM element.If the exposed methods depend on reactive values, include those values in the dependency array to avoid stale closures.
Common use cases include:
Prefer declarative props when possible. For example, use an isOpen prop to control a dialog instead of exposing open() and close() methods unless an imperative interface is genuinely needed.