🎯 Imperative Handles in React 19
In React, we usually build UIs in a declarative way. But sometimes, you may want to imperatively interact with a child component—for example:
To do this, React allows you to expose methods from a child component to the parent using a ref
. This ref
is created with useRef
and passed down to the child. Once inside the child, you can attach methods to it.
ref
type InputAPI = { focusInput: () => void }function MyInput({ref,...props}: React.InputHTMLAttributes<HTMLInputElement> & {ref: React.RefObject<InputAPI>}) {const inputRef = useRef<HTMLInputElement>(null)ref.current = {focusInput: () => inputRef.current?.focus(),}return <input ref={inputRef} {...props} />}function App() {const myInputRef = useRef<InputAPI>(null)return (<div><MyInput ref={myInputRef} placeholder="Enter your name" /><button onClick={() => myInputRef.current?.focusInput()}>Focus the input</button></div>)}
✅ This works, but has limitations:
useImperativeHandle
React provides a built-in hook—useImperativeHandle
—to safely expose imperative methods. In React 19, you can now access ref
directly as a prop, so there’s no need for forwardRef
.
type InputAPI = { focusInput: () => void }function MyInput({ref,...props}: React.InputHTMLAttributes<HTMLInputElement> & {ref: React.RefObject<InputAPI>}) {const inputRef = useRef<HTMLInputElement>(null)useImperativeHandle(ref,() => ({focusInput: () => inputRef.current?.focus(),}),[])return <input ref={inputRef} {...props} />}
Notice that we passed an empty dependency array to useImperativeHandle
. That’s fine—even if you’re using refs inside—because React refs don’t change between renders, so they don’t need to be included in dependencies.
📘 Learn more: Why you shouldn’t put refs in a dependency array
useImperativeHandle
is useful for:
⚠️ But keep in mind: imperative code should be a last resort. It’s harder to test, debug, and reason about. Prefer declarative solutions when possible.
📖 More on that here: Imperative vs Declarative Programming
useImperativeHandle
lets child components expose custom methods to their parent.ref
can be accessed directly from props
, so there’s no need to use forwardRef
.Quick Links
Legal Stuff
Social Media