Forwarding Refs in React JS
Forwarding Refs is a React technique that allows a parent component to pass a reference (ref) through a child component to a DOM element or another component. It is useful when a parent needs to perform imperative actions, such as focusing an input field, selecting text, scrolling to an element, or controlling a media element.
React JS Training: JustAcademy React JS Training Course
Course Demo: Register for React JS Course Demo
1. Learning Objectives
After completing this topic, you will be able to:
- Understand refs and ref forwarding in React.
- Learn how
useRef() and forwardRef() work.
- Pass refs from parent components to child components.
- Access DOM elements from parent components.
- Build reusable input and form components that support refs.
- Understand the difference between React 18 and React 19 ref handling.
- Use refs safely and follow React best practices.
2. What Is a Ref in React?
A ref is an object that allows a component to retain a reference to a DOM element or another value between renders. A ref created with useRef() contains a current property that can hold the referenced value.
Refs are commonly used when you need to interact directly with a DOM element instead of describing the behavior through React state and props.
Syntax
import { useRef } from "react";
const inputRef = useRef(null);
Initially, inputRef.current is null. After React attaches the ref to a DOM element, it contains that element.
Example: Focusing an Input
import { useRef } from "react";
export default function App() {
const inputRef = useRef(null);
function focusInput() {
inputRef.current?.focus();
}
return (
<div>
<input ref={inputRef} placeholder="Enter your name" />
<button type="button" onClick={focusInput}>
Focus Input
</button>
</div>
);
}
Explanation:
useRef(null) creates a ref object.
- The
ref attribute connects the object to the input element.
- When the button is clicked,
focusInput() runs.
- The input receives focus through
inputRef.current.focus().
3. What Is Forwarding Refs?
Forwarding refs means passing a ref received by a component to an element or another component inside it. This allows the parent component to access a particular element inside a reusable child component.
For example, a reusable CustomInput component may contain an HTML input. Ref forwarding lets the parent access that input without exposing the entire internal component structure.
Why Is Ref Forwarding Needed?
- To focus an input inside a custom component.
- To select text programmatically.
- To scroll to a particular DOM element.
- To control certain browser APIs or media elements.
- To build reusable form controls and component libraries.
- To expose a carefully limited set of imperative methods.
Important React Version Note
React 18 and earlier: Function components generally use forwardRef() to receive a ref from a parent.
React 19 and later: Function components can receive ref directly as a prop, so forwardRef() is no longer necessary for new function components. React documents forwardRef() as deprecated for future use, but existing code can still use it.
Reference: Official React forwardRef Documentation.
4. Understanding forwardRef()
The forwardRef() API wraps a render function so that a function component can receive the ref passed by its parent as a second argument.
Syntax
import { forwardRef } from "react";
const CustomInput = forwardRef(function CustomInput(props, ref) {
return <input {...props} ref={ref} />;
});
export default CustomInput;
Parameters
| Parameter |
Description |
props |
Contains the properties passed from the parent component. |
ref |
The ref received from the parent component. |
forwardRef() |
Creates a component that can receive and forward a ref in React 18 and earlier. |
How It Works
- The parent creates a ref using
useRef().
- The parent passes the ref to the child component.
- The child receives the ref through
forwardRef().
- The child attaches the ref to an internal DOM element.
- The parent can access that DOM element through
ref.current.
Flow Diagram
Parent Component
|
| Creates useRef()
v
Passes ref to Child Component
|
v
Child Receives ref Using forwardRef()
|
v
Attaches ref to DOM Element
|
v
Parent Accesses DOM Element
5. Example: Forwarding a Ref to an Input
This example uses the React 18-compatible forwardRef() pattern.
CustomInput.jsx
import { forwardRef } from "react";
const CustomInput = forwardRef(function CustomInput(props, ref) {
return (
<input
{...props}
ref={ref}
className="custom-input"
/>
);
});
export default CustomInput;
App.jsx
import { useRef } from "react";
import CustomInput from "./CustomInput";
export default function App() {
const inputRef = useRef(null);
function handleFocus() {
inputRef.current?.focus();
}
function handleClear() {
if (inputRef.current) {
inputRef.current.value = "";
inputRef.current.focus();
}
}
return (
<div>
<h2>Forwarding Refs Example</h2>
<CustomInput
ref={inputRef}
type="text"
placeholder="Enter your name"
/>
<button type="button" onClick={handleFocus}>
Focus Input
</button>
<button type="button" onClick={handleClear}>
Clear Input
</button>
</div>
);
}
Explanation
CustomInput is a reusable input component.
- The parent passes
inputRef to CustomInput.
forwardRef() receives the ref and attaches it to the actual input element.
- The parent can call
focus() or access the input value through inputRef.current.
Note: Directly changing an input's value is appropriate for this simple uncontrolled-input demonstration. For a controlled input, update its React state instead of changing the DOM value directly.
6. Forwarding Refs Through Multiple Components
Sometimes a ref must pass through more than one component before reaching the target DOM element. Each intermediate component must forward the ref in React 18 and earlier.
Example: App.jsx
import { useRef } from "react";
import FormField from "./FormField";
export default function App() {
const inputRef = useRef(null);
return (
<div>
<FormField
ref={inputRef}
label="Email Address"
placeholder="Enter your email"
/>
<button
type="button"
onClick={() => inputRef.current?.focus()}
>
Focus Email Field
</button>
</div>
);
}
FormField.jsx
import { forwardRef } from "react";
const FormField = forwardRef(function FormField(props, ref) {
const { label, ...inputProps } = props;
return (
<label>
{label}
<input {...inputProps} ref={ref} />
</label>
);
});
export default FormField;
The parent passes the ref to FormField, and FormField forwards it to the input. The parent can then focus the input directly.
7. Forwarding Refs in React 19
React 19 allows function components to receive ref directly as a prop. This makes ref forwarding simpler because a new function component does not need to be wrapped in forwardRef().
CustomInput.jsx
function CustomInput({ ref, ...props }) {
return <input {...props} ref={ref} />;
}
export default CustomInput;
App.jsx
import { useRef } from "react";
import CustomInput from "./CustomInput";
export default function App() {
const inputRef = useRef(null);
return (
<div>
<CustomInput
ref={inputRef}
placeholder="Enter your name"
/>
<button
type="button"
onClick={() => inputRef.current?.focus()}
>
Focus Input
</button>
</div>
);
}
Important: The direct ref prop approach for function components requires React 19 or later. If your project uses React 18 or earlier, use forwardRef().
React 18 vs React 19
| Feature |
React 18 and Earlier |
React 19 and Later |
| Receiving ref in a function component |
Use forwardRef(). |
Receive ref as a prop. |
| Passing ref to a DOM element |
Attach the forwarded ref. |
Attach the ref prop directly. |
| Existing forwardRef code |
Supported. |
Still supported, but no longer necessary for new function components. |
| Recommended approach for new function components |
forwardRef(). |
Direct ref prop. |
8. Using forwardRef with useImperativeHandle
The useImperativeHandle() Hook lets a component control which methods or values a parent can access through a ref. Instead of exposing the entire DOM element, a component can expose only a small, specific API.
Example: Exposing Focus and Clear Methods
import {
forwardRef,
useImperativeHandle,
useRef
} from "react";
const CustomInput = forwardRef(function CustomInput(props, ref) {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
focus() {
inputRef.current?.focus();
},
clear() {
if (inputRef.current) {
inputRef.current.value = "";
}
}
}), []);
return <input {...props} ref={inputRef} />;
});
export default CustomInput;
Parent Component
import { useRef } from "react";
import CustomInput from "./CustomInput";
export default function App() {
const customInputRef = useRef(null);
return (
<div>
<CustomInput
ref={customInputRef}
placeholder="Enter text"
/>
<button
type="button"
onClick={() => customInputRef.current?.focus()}
>
Focus
</button>
<button
type="button"
onClick={() => customInputRef.current?.clear()}
>
Clear
</button>
</div>
);
}
In this example, the parent receives an object with only two methods: focus() and clear(). It cannot directly access the internal input DOM node through this ref.
React 19 note: You can also use useImperativeHandle() with a ref received directly as a prop, without wrapping the component in forwardRef().
9. Common Use Cases of Ref Forwarding
| Use Case |
Example |
| Input focus |
Focus a search box when a page loads or a button is clicked. |
| Text selection |
Select the text inside an input or textarea. |
| Scrolling |
Scroll to a particular section of a page. |
| Media control |
Call browser methods on a video or audio element. |
| Reusable components |
Allow a parent to focus an input inside a custom form control. |
| Imperative APIs |
Expose a limited set of methods through useImperativeHandle(). |
10. Refs vs Props vs State
| Feature |
Refs |
Props |
State |
| Purpose |
Access DOM elements or store values between renders. |
Pass information from parent to child. |
Store data that affects the rendered UI. |
| Triggers re-render when changed |
No, changing ref.current alone does not. |
New prop values can trigger rendering. |
State updates schedule a render. |
| Mutability |
ref.current is mutable. |
Read-only from the receiving component's perspective. |
Updated using state setters or a reducer. |
| Typical example |
Focus an input. |
Pass a label or placeholder. |
Track a selected tab or form value. |
11. Best Practices for Ref Forwarding
- Use refs for imperative tasks that cannot be expressed naturally through props.
- Prefer props and state for normal UI behavior, such as opening a modal or changing a selected tab.
- Forward a ref to the intended DOM element or component; otherwise, the parent's ref may remain
null.
- Use optional chaining, such as
inputRef.current?.focus(), when the element may not be mounted.
- Avoid exposing internal DOM elements unnecessarily from high-level application components.
- Use
useImperativeHandle() when you need to expose a small, controlled set of methods.
- Keep rendering logic pure and do not perform DOM operations during render.
- Choose the ref syntax that matches your project's React version.
- Remember that conditional rendering may mean the referenced element does not currently exist.
12. Common Mistakes and Solutions
| Mistake |
Why It Happens |
Solution |
| Ref is always null |
The ref was not attached to a DOM element or forwarded correctly. |
Pass the ref through the child and attach it to the target. |
| Using forwardRef incorrectly in React 18 |
The ref is not received as an ordinary prop by a function component. |
Use forwardRef() and accept the ref as the second argument. |
| Using direct ref props in React 18 |
React 18 function components cannot receive refs as regular props in the React 19 manner. |
Use the React 18-compatible forwardRef() pattern. |
| Expecting a ref update to re-render the component |
Changing ref.current does not trigger a render. |
Use state if the changed value must update the UI. |
| Exposing too much of the DOM |
The parent becomes tightly coupled to child internals. |
Expose a limited API with useImperativeHandle() when appropriate. |
| Using refs for normal UI state |
UI changes are stored outside React state. |
Use useState() or useReducer() for values that determine rendered output. |
13. Practical Project: Reusable Form with Forwarded Refs
Project Goal: Build a form with reusable input components that can be focused programmatically.
Requirements
- Create a reusable custom input component.
- Forward refs from the parent to the input elements.
- Focus the name field when the user clicks a button.
- Focus the email field when another button is clicked.
- Validate the form using normal React state or browser form validation.
Suggested Folder Structure
src/
App.jsx
CustomInput.jsx
styles.css
Expected Learning Outcome
You will understand how refs pass through reusable components and how parents can interact with selected DOM elements without making every input implementation part of the parent component.
14. Interview Questions and Answers
Q1. What is ref forwarding in React?
Ref forwarding allows a component to pass a ref it receives to a DOM element or another component inside it.
Q2. Why is forwardRef used?
In React 18 and earlier, forwardRef() allows a function component to receive a ref from its parent and attach it to an internal DOM element.
Q3. What is useRef?
useRef() is a React Hook that creates a persistent ref object with a mutable current property.
Q4. Does changing ref.current trigger a re-render?
No. Updating ref.current alone does not trigger a re-render.
Q5. Can a ref access a DOM element inside a child component?
Yes. The child must expose or forward the ref to that element, using the appropriate approach for the React version.
Q6. What is useImperativeHandle?
useImperativeHandle() lets a component customize the methods or values exposed through a ref.
Q7. Is forwardRef required in React 19?
No. React 19 function components can receive ref as a prop. The existing forwardRef() API remains supported, although it is no longer necessary for new function components.
Q8. What is the difference between refs and state?
Refs store values or DOM references without triggering renders when changed. State stores data that affects the UI and triggers rendering when updated.
Q9. What happens if a ref is not attached to any element?
The ref's current value will typically remain null, or become null when its element is removed.
Q10. Should refs be used for all DOM operations?
No. Use refs when imperative access is necessary, such as focusing, scrolling, selecting text, or controlling media. Prefer declarative props and state for ordinary UI behavior.
15. Practice Exercises
- Create a custom input component using
forwardRef().
- Build a parent component that focuses the child input on a button click.
- Create a reusable textarea component that supports text selection.
- Forward a ref through two component layers to reach an input element.
- Use
useImperativeHandle() to expose only focus() and clear() methods.
- Rewrite a simple example using React 19's direct
ref prop.
- Compare the React 18 and React 19 implementations and identify the version-specific differences.
16. Quick Revision
- A ref provides access to a DOM node or a persistent mutable value.
useRef() creates a ref object in function components.
forwardRef() allows function components to receive and forward refs in React 18 and earlier.
- React 19 supports receiving
ref directly as a prop in function components.
- Refs are useful for focus, scrolling, text selection, and other imperative operations.
useImperativeHandle() can limit the API exposed through a ref.
- Updating
ref.current does not trigger a re-render.
- Use state and props for ordinary declarative UI behavior.
17. Official React Documentation
18. React JS Training Resources
Explore more React JS concepts, Hooks, component design, and practical development through these course resources:
Conclusion
Ref forwarding helps reusable React components expose specific DOM elements or imperative methods to their parent components. Understanding useRef(), forwardRef(), and useImperativeHandle() makes it easier to build flexible form controls and reusable UI components. For modern React development, remember that React 19 supports passing refs directly as props to function components, while forwardRef() remains relevant to existing projects and compatibility with older React versions.