useImperativeHandle Hook — Controlling Components via Refs
In React, data typically flows from top to bottom via props. However, sometimes it's necessary for a parent component to directly call child methods or functions. For example: opening or closing a modal, scrolling a list, or resetting an input value. In such cases, React provides the useImperativeHandle hook to work with forwardRef.
🔹 What is useImperativeHandle
useImperativeHandle(ref, createHandle, deps)
instructs React about what the parent should see when using a ref on a given component.
- ref — the ref passed via
forwardRef. - createHandle — function that returns an object with the methods and values we want to expose.
- deps — dependency array (optional) to control handle updates.
📍 Example 1 — Parent Controls Child's Focus
Let's create a component that contains an input and its own focus method. The parent component will be able to call this method via ref.
// CustomInput.js
import React, { useRef, forwardRef, useImperativeHandle } from 'react';
const CustomInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
clear: () => {
inputRef.current.value = '';
}
}));
return (
<input
ref={inputRef}
type="text"
placeholder="Write something..."
/>
);
});
export default CustomInput;
Usage in Parent
// App.js
import React, { useRef } from 'react';
import CustomInput from './CustomInput';
export default function App() {
const inputRef = useRef();
return (
<div>
<CustomInput ref={inputRef} />
<br />
<button onClick={() => inputRef.current.focus()}>Focus</button>
<button onClick={() => inputRef.current.clear()}>Clear</button>
</div>
);
}
Here the parent (App) can call the child's (CustomInput) methods —
focus() and clear(), without passing props.
Thus, we created a small "public API" for the child.
🧠 Why useImperativeHandle is Needed
Without useImperativeHandle, React would give the parent the DOM node via ref (e.g., reference to the input). But in many cases, that's not sufficient. Through useImperativeHandle, we can give the parent only the methods or data we want them to control.
This approach is especially useful when you want to limit the parent's access to internal logic.
📍 Example 2 — Parent Controlling Modal Component
This is a professional example where the parent opens and closes a modal via ref.
// Modal.js
import React, { useState, forwardRef, useImperativeHandle } from 'react';
const Modal = forwardRef((props, ref) => {
const [isOpen, setIsOpen] = useState(false);
useImperativeHandle(ref, () => ({
open: () => setIsOpen(true),
close: () => setIsOpen(false)
}));
if (!isOpen) return null;
return (
<div style={{
position: 'fixed',
top: 0, left: 0, right: 0, bottom: 0,
background: 'rgba(0,0,0,0.5)',
display: 'flex', justifyContent: 'center', alignItems: 'center'
}}>
<div style={{ background: 'white', padding: '20px', borderRadius: '8px' }}>
<h3>Modal Window</h3>
<button onClick={() => setIsOpen(false)}>Close</button>
</div>
</div>
);
});
export default Modal;
// App.js
import React, { useRef } from 'react';
import Modal from './Modal';
export default function App() {
const modalRef = useRef();
return (
<div>
<button onClick={() => modalRef.current.open()}>Open Modal</button>
<Modal ref={modalRef} />
</div>
);
}
In this example, the parent completely controls the child's behavior without props. It's very similar to the imperative "openModal()" or "closeModal()" function style.
⚙️ Best Practices
- Use useImperativeHandle only when you need to control internal logic from the parent.
- Don't pass the entire DOM ref to the parent; define only necessary methods.
- Don't use this hook for data flow — it's not designed for state exchange.
🧩 Advanced Example — Limited Access via Ref
Sometimes you want the parent to not be able to access all internal methods. useImperativeHandle allows you to "hide" the rest.
useImperativeHandle(ref, () => ({
open: () => setVisible(true),
close: () => setVisible(false)
}), []); // only two methods accessible to parent
This way, the parent cannot change state or DOM directly.
🎯 Exercise (Try it yourself)
Write a component that:
- Has an input field.
- Has focus() and reset() methods defined via useImperativeHandle.
- The parent can call these methods via ref.
📘 Summary
- useImperativeHandle — React's advanced hook for defining the child ref's "public API".
- Works only with forwardRef.
- Useful for modal, tooltip, input, and scroll control components.
- Helps hide internal logic from parent access.