The Power of forwardRef in React.js: When and How to Use It

Just a guy who loves to write code and watch anime.
Introduction
React.js makes it easier to create user interfaces, but it can be difficult to directly access the DOM and component instances. forwardRef helps solve this problem by making it simpler to pass and manage references.
Direct Access Challenges in Component Libraries
Let's say we're developing a CustomInput component to expose from our library, intending to let developers control focus or integrate with third-party libraries requiring direct DOM access. A common challenge arises when trying to allow the parent component to directly interact with the CustomInput's underlying DOM node.
Consider the initial approach without forwardRef:
// CustomInput component without forwardRef
function CustomInput(props) {
// Attempting direct DOM access
return <input {...props} />;
}
// Attempting to use ref in a parent component
function ParentComponent() {
const inputRef = React.createRef();
// Attempt to focus the input on button click fails
// because CustomInput doesn't forward the ref
const focusInput = () => {
inputRef.current.focus(); // Error: inputRef.current is null
};
return (
<>
<CustomInput ref={inputRef} />
<button onClick={focusInput}>Focus the input</button>
</>
);
}
In this scenario, the parent component's attempt to focus the CustomInput fails because ref is not automatically passed through. Older solutions, such as using props to pass down callbacks or using context, make things more complicated and hide the intended direct control features.
These challenges reveal React's initial refs limitations, emphasizing the need for forwardRef to improve component interaction and library usability.
Introduction of forwardRef
forwardRef lets refs be sent to a DOM node or component instance inside a component, making direct interactions easier and helping components work together better.
How forwardRef Works
forwardRef wraps a component, enabling it to receive a ref and forward it to a child DOM element or component. This is crucial for operations requiring direct access, like input focus or animation control.
Practical Example
Using forwardRef simplifies focusing on a nested button component:
import React, { forwardRef } from 'react';
const FancyButton = forwardRef((props, ref) => (
<button ref={ref} {...props}>{props.children}</button>
));
// Parent component using FancyButton
const ParentComponent = () => {
const buttonRef = React.useRef(null);
const focusButton = () => buttonRef.current.focus();
return (
<>
<FancyButton ref={buttonRef}>Click Me</FancyButton>
<button onClick={focusButton}>Focus the Fancy Button</button>
</>
);
};
Appropriate Use Cases for forwardRef
forwardRef is especially helpful for working with direct DOM access, managing focus, animations, and integrating with libraries focused on the DOM.
It often comes in handy for making reusable component libraries that need encapsulation and direct instance access.
However, be careful with overusing refs, as the React documentation says:
Do not overuse refs. You should only use refs for imperative behaviors that you can’t express as props: for example, scrolling to a node, focusing a node, triggering an animation, selecting text, and so on.
If you can express something as a prop, you should not use a ref. For example, instead of exposing an imperative handle like
{ open, close }from aModalcomponent, it is better to takeisOpenas a prop like<Modal isOpen={isOpen} />.






