How to Update Forwarding Refs in React
Updating forwarding refs in React allows parent components to
directly access, modify, and respond to changes in the DOM nodes of
child components. This guide explains how to implement ref forwarding
using React’s forwardRef API, how to update ref values
dynamically, and how to use callback refs or the
useImperativeHandle hook to customize ref updates.
Understanding Ref Forwarding
Ref forwarding is a technique for automatically passing a ref through a component to one of its children. This is particularly useful for reusable component libraries where parent components need to manage focus, selection, or animations of specific child DOM elements.
Here is a basic implementation of ref forwarding:
import { forwardRef } from 'react';
const MyInput = forwardRef((props, ref) => {
return <input ref={ref} {...props} />;
});
export default MyInput;In this setup, when a parent component passes a ref to
MyInput, the ref is forwarded directly to the HTML
<input> element. React automatically updates the
ref’s .current property to point to the DOM element once it
mounts.
Updating Forwarded Refs Dynamically
In React, the ref object is mutable. The most common way
to update a forwarded ref is by interacting with its
.current property.
1. Direct DOM Manipulation
Once the ref is forwarded and attached, the parent component can update the DOM node directly:
import { useRef } from 'react';
import MyInput from './MyInput';
function ParentComponent() {
const inputRef = useRef(null);
const focusAndClearInput = () => {
if (inputRef.current) {
inputRef.current.focus();
inputRef.current.value = ''; // Direct update to the DOM node
}
};
return (
<div>
<MyInput ref={inputRef} />
<button onClick={focusAndClearInput}>Clear and Focus</button>
</div>
);
}2. Using Callback Refs for Side Effects on Updates
If you need to run code when React updates the ref (for example, when
the node attaches or detaches), use a callback ref
instead of a standard useRef object.
A callback ref is a function passed to the ref prop.
React calls this function with the DOM element when it mounts, and with
null when it unmounts.
import { useCallback } from 'react';
import MyInput from './MyInput';
function ParentComponent() {
const measuredRef = useCallback((node) => {
if (node !== null) {
console.log('The input ref has updated. Height is:', node.getBoundingClientRect().height);
}
}, []);
return <MyInput ref={measuredRef} />;
}3.
Customizing the Ref Value with useImperativeHandle
Sometimes you do not want to expose the entire DOM node to the
parent. Instead, you can update and customize the exposed ref instance
using the useImperativeHandle hook inside the child
component.
This allows you to define custom methods that the parent can call to update the child component’s state or trigger specific behaviors.
import { forwardRef, useImperativeHandle, useRef } from 'react';
const CustomInput = forwardRef((props, ref) => {
const inputRef = useRef(null);
// Expose custom update functions to the parent
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
setValue: (val) => {
inputRef.current.value = val;
}
}));
return <input ref={inputRef} {...props} />;
});
// Parent usage:
// inputRef.current.focus();
// inputRef.current.setValue('New Value');Note on React 19 Updates
If you are using React 19 or later, forwardRef is no
longer strictly necessary for standard components. You can pass
ref as a normal prop directly into functional components.
However, the underlying mechanics of updating the ref via
.current or using useImperativeHandle remain
identical.