Deprecated
In React 19,forwardRef is no longer necessary. Passref as a prop instead.
forwardRef will be deprecated in a future release. Learn morehere.
forwardRef lets your component expose a DOM node to the parent component with aref.
constSomeComponent =forwardRef(render)Reference
forwardRef(render)
CallforwardRef() to let your component receive a ref and forward it to a child component:
import{forwardRef}from'react';
constMyInput =forwardRef(functionMyInput(props,ref){
// ...
});Parameters
render: The render function for your component. React calls this function with the props andrefthat your component received from its parent. The JSX you return will be the output of your component.
Returns
forwardRef returns a React component that you can render in JSX. Unlike React components defined as plain functions, a component returned byforwardRef is also able to receive aref prop.
Caveats
- In Strict Mode, React willcall your render function twice in order tohelp you find accidental impurities. This is development-only behavior and does not affect production. If your render function is pure (as it should be), this should not affect the logic of your component. The result from one of the calls will be ignored.
render function
forwardRef accepts a render function as an argument. React calls this function withprops andref:
constMyInput =forwardRef(functionMyInput(props,ref){
return(
<label>
{props.label}
<inputref={ref}/>
</label>
);
});Parameters
props: The props passed by the parent component.ref: Therefattribute passed by the parent component. Therefcan be an object or a function. If the parent component has not passed a ref, it will benull. You should either pass therefyou receive to another component, or pass it touseImperativeHandle.
Returns
forwardRef returns a React component that you can render in JSX. Unlike React components defined as plain functions, the component returned byforwardRef is able to take aref prop.
Usage
Exposing a DOM node to the parent component
By default, each component’s DOM nodes are private. However, sometimes it’s useful to expose a DOM node to the parent—for example, to allow focusing it. To opt in, wrap your component definition intoforwardRef():
import{forwardRef}from'react';
constMyInput =forwardRef(functionMyInput(props,ref){
const{label,...otherProps} =props;
return(
<label>
{label}
<input{...otherProps}/>
</label>
);
});You will receive aref as the second argument after props. Pass it to the DOM node that you want to expose:
import{forwardRef}from'react';
constMyInput =forwardRef(functionMyInput(props,ref){
const{label,...otherProps} =props;
return(
<label>
{label}
<input{...otherProps}ref={ref}/>
</label>
);
});This lets the parentForm component access the<input> DOM node exposed byMyInput:
functionForm(){
constref =useRef(null);
functionhandleClick(){
ref.current.focus();
}
return(
<form>
<MyInputlabel="Enter your name:"ref={ref}/>
<buttontype="button"onClick={handleClick}>
Edit
</button>
</form>
);
}ThisForm componentpasses a ref toMyInput. TheMyInput componentforwards that ref to the<input> browser tag. As a result, theForm component can access that<input> DOM node and callfocus() on it.
Keep in mind that exposing a ref to the DOM node inside your component makes it harder to change your component’s internals later. You will typically expose DOM nodes from reusable low-level components like buttons or text inputs, but you won’t do it for application-level components like an avatar or a comment.
Example 1 of 2:Focusing a text input
Clicking the button will focus the input. TheForm component defines a ref and passes it to theMyInput component. TheMyInput component forwards that ref to the browser<input>. This lets theForm component focus the<input>.
import{useRef}from'react';importMyInputfrom'./MyInput.js';exportdefaultfunctionForm(){constref =useRef(null);functionhandleClick(){ref.current.focus();}return(<form><MyInputlabel="Enter your name:"ref={ref}/><buttontype="button"onClick={handleClick}> Edit</button></form>);}
Forwarding a ref through multiple components
Instead of forwarding aref to a DOM node, you can forward it to your own component likeMyInput:
constFormField =forwardRef(functionFormField(props,ref){
// ...
return(
<>
<MyInputref={ref}/>
...
</>
);
});If thatMyInput component forwards a ref to its<input>, a ref toFormField will give you that<input>:
functionForm(){
constref =useRef(null);
functionhandleClick(){
ref.current.focus();
}
return(
<form>
<FormFieldlabel="Enter your name:"ref={ref}isRequired={true}/>
<buttontype="button"onClick={handleClick}>
Edit
</button>
</form>
);
}TheForm component defines a ref and passes it toFormField. TheFormField component forwards that ref toMyInput, which forwards it to a browser<input> DOM node. This is howForm accesses that DOM node.
import{useRef}from'react';importFormFieldfrom'./FormField.js';exportdefaultfunctionForm(){constref =useRef(null);functionhandleClick(){ref.current.focus();}return(<form><FormFieldlabel="Enter your name:"ref={ref}isRequired={true}/><buttontype="button"onClick={handleClick}> Edit</button></form>);}
Exposing an imperative handle instead of a DOM node
Instead of exposing an entire DOM node, you can expose a custom object, called animperative handle, with a more constrained set of methods. To do this, you’d need to define a separate ref to hold the DOM node:
constMyInput =forwardRef(functionMyInput(props,ref){
constinputRef =useRef(null);
// ...
return<input{...props}ref={inputRef}/>;
});Pass theref you received touseImperativeHandle and specify the value you want to expose to theref:
import{forwardRef,useRef,useImperativeHandle}from'react';
constMyInput =forwardRef(functionMyInput(props,ref){
constinputRef =useRef(null);
useImperativeHandle(ref,()=>{
return{
focus(){
inputRef.current.focus();
},
scrollIntoView(){
inputRef.current.scrollIntoView();
},
};
},[]);
return<input{...props}ref={inputRef}/>;
});If some component gets a ref toMyInput, it will only receive your{ focus, scrollIntoView } object instead of the DOM node. This lets you limit the information you expose about your DOM node to the minimum.
import{useRef}from'react';importMyInputfrom'./MyInput.js';exportdefaultfunctionForm(){constref =useRef(null);functionhandleClick(){ref.current.focus();// This won't work because the DOM node isn't exposed:// ref.current.style.opacity = 0.5;}return(<form><MyInputplaceholder="Enter your name"ref={ref}/><buttontype="button"onClick={handleClick}> Edit</button></form>);}
Read more about using imperative handles.
Pitfall
Do not overuse refs. You should only use refs forimperative 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 aModal component, it is better to takeisOpen as a prop like<Modal isOpen={isOpen} />.Effects can help you expose imperative behaviors via props.
Troubleshooting
My component is wrapped inforwardRef, but theref to it is alwaysnull
This usually means that you forgot to actually use theref that you received.
For example, this component doesn’t do anything with itsref:
constMyInput =forwardRef(functionMyInput({label},ref){
return(
<label>
{label}
<input/>
</label>
);
});To fix it, pass theref down to a DOM node or another component that can accept a ref:
constMyInput =forwardRef(functionMyInput({label},ref){
return(
<label>
{label}
<inputref={ref}/>
</label>
);
});Theref toMyInput could also benull if some of the logic is conditional:
constMyInput =forwardRef(functionMyInput({label,showInput},ref){
return(
<label>
{label}
{showInput &&<inputref={ref}/>}
</label>
);
});IfshowInput isfalse, then the ref won’t be forwarded to any node, and a ref toMyInput will remain empty. This is particularly easy to miss if the condition is hidden inside another component, likePanel in this example:
constMyInput =forwardRef(functionMyInput({label,showInput},ref){
return(
<label>
{label}
<PanelisExpanded={showInput}>
<inputref={ref}/>
</Panel>
</label>
);
});