Forward Ref in React

October 30, 2024

You would use forwardRef in React when you need to pass a ref from a parent component to a child component in order to directly interact with the child's underlying DOM element or its internal state. Typically, refs can only be attached to DOM elements (like input, div, etc.), but forwardRef enables you to expose the internal elements or methods of custom components to parent components.

Here are a few cases where you might need forwardRef:

  1. Accessing a Child's DOM Element from a Parent Component When a parent component needs to directly manipulate a child component's DOM element (e.g., focus, scroll, measure dimensions), forwardRef allows the parent to pass the ref to the child's internal DOM element.

Example: If you create a custom InputField component but want to allow the parent to focus on the input element:

1// Child component
2const InputField = forwardRef((props, ref) => {
3 return <input ref={ref} {...props} />; // Passing the ref to the internal DOM element
4});
5
6// Parent component
7function Form() {
8 const inputRef = useRef(); // Ref to be passed
9
10 const handleClick = () => {
11 inputRef.current.focus(); // Directly focusing the child input
12 };
13
14 return (
15 <div>
16 <InputField ref={inputRef} placeholder="Enter your name" />
17 <button onClick={handleClick}>Focus Input</button>
18 </div>
19 );
20}
21
22

Without forwardRef, the parent wouldn't be able to access the child’s input element directly.

  1. Creating Reusable Component Libraries If you're building a component library (like UI frameworks or reusable form components), you might need to provide access to internal DOM elements, like input fields, so users of your library can control these elements. forwardRef ensures users can still use ref to interact with the DOM.

Example: A Custom Button Component You might create a custom button that still exposes the native button's functionality:

1const CustomButton = forwardRef((props, ref) => (
2 <button ref={ref} className="custom-btn" {...props}>
3 {props.children}
4 </button>
5));
6
7function App() {
8 const buttonRef = useRef();
9
10 const handleClick = () => {
11 buttonRef.current.disabled = true; // Disabling the button from the parent
12 };
13
14 return (
15 <div>
16 <CustomButton ref={buttonRef}>Click Me</CustomButton>
17 <button onClick={handleClick}>Disable Custom Button</button>
18 </div>
19 );
20}
21
22

Here, forwardRef allows the parent to disable or manipulate the button directly.

  1. Exposing Custom Methods in Child Components Using forwardRef together with useImperativeHandle, you can expose custom methods that the parent component can call on the child component. This can be useful if a parent needs to control the behavior of the child component beyond just accessing its DOM elements.

Example: Custom Input with Exposed Methods

1import { useImperativeHandle, forwardRef, useRef } from 'react';
2
3// Child component that exposes custom methods to parent
4const CustomInput = forwardRef((props, ref) => {
5 const inputRef = useRef();
6
7 useImperativeHandle(ref, () => ({
8 focus: () => {
9 inputRef.current.focus();
10 },
11 clear: () => {
12 inputRef.current.value = '';
13 }
14 }));
15
16 return <input ref={inputRef} {...props} />;
17});
18
19// Parent component that uses the exposed methods
20function App() {
21 const inputRef = useRef();
22
23 return (
24 <div>
25 <CustomInput ref={inputRef} placeholder="Enter text" />
26 <button onClick={() => inputRef.current.focus()}>Focus</button>
27 <button onClick={() => inputRef.current.clear()}>Clear</button>
28 </div>
29 );
30}
31
32

The parent can now call inputRef.current.focus() or inputRef.current.clear() to interact with the custom input.

  1. Integrating with Third-Party Libraries When you are wrapping third-party UI libraries or custom DOM elements (like maps, charts, or other interactive widgets), you may need forwardRef to expose a ref so that the parent can interact with the third-party library via its underlying DOM node.

Example: Wrapping a Third-Party Chart Library

1import { forwardRef, useEffect, useRef } from 'react';
2
3// Wrapping a chart library that requires direct DOM access
4const Chart = forwardRef((props, ref) => {
5 const chartRef = useRef(); // Internally, the ref to the DOM
6
7 useEffect(() => {
8 // Initialize chart library with the DOM node
9 initializeChart(chartRef.current);
10 }, []);
11
12 return <div ref={chartRef} {...props}></div>; // Attach the ref to the chart's container
13});
14
15// Parent can manipulate the chart container (e.g., reset, update)
16function App() {
17 const chartContainerRef = useRef();
18
19 const updateChart = () => {
20 // Interact with the chart's container, e.g., redraw or resize
21 chartContainerRef.current.style.width = "800px";
22 };
23
24 return (
25 <div>
26 <Chart ref={chartContainerRef} />
27 <button onClick={updateChart}>Update Chart Width</button>
28 </div>
29 );
30}
31
32

The parent can now access the chart's underlying DOM element and control its size or behavior directly.

When You Wouldn't Need forwardRef: If the parent doesn't need to interact with a child’s internal DOM element or methods. If you are simply passing data or state between components without needing direct DOM interaction. For most cases where the child component doesn't need to expose its DOM or custom methods, you can manage everything via props. In short, use forwardRef when you need to pass a ref to a custom component so the parent can directly interact with the child’s DOM element or custom methods.

Share this article