HomeAbout Me

Advanced React APIs: Imperative Handles

By Daniel Nguyen
Published in React JS
June 17, 2025
2 min read
Advanced React APIs: Imperative Handles

Mastering useImperativeHandle in React: When and How to Use It

React is famous for its declarative approach—describing what the UI should look like based on state. But sometimes, you need to step outside that model and tell React what to do directly. That’s where imperative handles come in.

In this post, we’ll explore how to expose functions from a child component to its parent using useImperativeHandle, understand when and why to use it, and walk through a practical example of implementing a Scrollable component with exposed scrollToTop and scrollToBottom methods.


🚦 Why Would You Need an Imperative Handle?

There are situations where a parent component needs to directly control a child component. For example:

  • Focusing an input field
  • Scrolling a container
  • Triggering an animation or method inside the child

React offers a solution via refs. Normally, ref is used to directly access a DOM node, but you can also use it to expose methods from a component to its parent.


❌ A Common But Flawed Approach

You might be tempted to do something like this:

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} />
}

This works, but it’s not safe with concurrent features or callback refs. React warns against mutating the .current property of refs in this way inside child components.


✅ The Right Way: useImperativeHandle

React provides the useImperativeHandle hook to safely expose methods to a parent through a ref.

✅ Safe Version:

type InputAPI = { focusInput: () => void }
const MyInput = React.forwardRef<InputAPI, React.InputHTMLAttributes<HTMLInputElement>>(
(props, ref) => {
const inputRef = useRef<HTMLInputElement>(null)
useImperativeHandle(ref, () => ({
focusInput: () => inputRef.current?.focus(),
}), [])
return <input ref={inputRef} {...props} />
}
)

🔒 useImperativeHandle makes your component API safer and better encapsulated.


🔍 Dependency Array Note

You might notice that inputRef is used inside the function passed to useImperativeHandle, but it’s not listed in the dependency array. That’s because you don’t need to include refs—they are stable and won’t change between renders.

Learn more about this here →


✨ Real Example: A Scrollable Component

Let’s say we’re building a component that wraps a scrollable container and gives parent components the ability to scroll to the top or bottom.

🧱 The Task

You’re given a Scrollable component with two already-implemented methods: scrollToTop() and scrollToBottom(). Your job is to expose these methods to the parent using useImperativeHandle.

💡 Implementation

type ScrollAPI = {
scrollToTop: () => void
scrollToBottom: () => void
}
const Scrollable = React.forwardRef<ScrollAPI, React.HTMLAttributes<HTMLDivElement>>(
(props, ref) => {
const containerRef = useRef<HTMLDivElement>(null)
const scrollToTop = () => {
containerRef.current?.scrollTo({ top: 0, behavior: 'smooth' })
}
const scrollToBottom = () => {
containerRef.current?.scrollTo({
top: containerRef.current.scrollHeight,
behavior: 'smooth',
})
}
useImperativeHandle(ref, () => ({
scrollToTop,
scrollToBottom,
}), [])
return (
<div
ref={containerRef}
style={{ overflowY: 'auto', maxHeight: '200px', border: '1px solid #ccc' }}
{...props}
/>
)
}
)

🧪 Using the Scrollable Component

function App() {
const scrollRef = useRef<ScrollAPI>(null)
return (
<div>
<button onClick={() => scrollRef.current?.scrollToTop()}>Scroll to Top</button>
<button onClick={() => scrollRef.current?.scrollToBottom()}>Scroll to Bottom</button>
<Scrollable ref={scrollRef}>
{/* Long content here */}
<div style={{ height: '600px' }}>
<p>Lots of content here...</p>
</div>
</Scrollable>
</div>
)
}

🧠 When Should You Use useImperativeHandle?

🛑 Don’t overuse it! Most of the time, you shouldn’t need it.

Prefer declarative solutions. Only use useImperativeHandle when:

  • You can’t express behavior using props or state.
  • The parent needs to call an action on the child (focus, scroll, reset, etc.).

Read more about this concept here: 👉 Imperative vs Declarative Programming


📝 Summary

  • useRef stores a reference that persists across renders.
  • useImperativeHandle lets you expose methods to parent components safely.
  • Always wrap imperative handles with React.forwardRef.
  • Only use this pattern when necessary—keep your code declarative when possible.

By understanding and using useImperativeHandle, you unlock more advanced control patterns in React—just remember to wield it responsibly!



Tags

#AdvancedReactAPIs

Share

Previous Article
Advanced React APIs: Layout Computation

Table Of Contents

1
🚦 Why Would You Need an Imperative Handle?
2
❌ A Common But Flawed Approach
3
✅ The Right Way: useImperativeHandle
4
🔍 Dependency Array Note
5
✨ Real Example: A Scrollable Component
6
🧠 When Should You Use useImperativeHandle?
7
📝 Summary

Related Posts

Advanced React APIs: Sync Exernal Store
June 19, 2025
2 min
© 2025, All Rights Reserved.
Powered By

Quick Links

About Me

Legal Stuff

Social Media