Sign in
Build 10x products in minutes by chatting with AI - beyond just a prototype.
In traditional React (preâReact 19), you usedâŻReact.forwardRef
to âforwardâ a ref from a parent component down to a child (typically a DOM node) when the child was a function component. This allowed the parent to directly manipulate or read the underlying DOM node (or even call methods on a child) without breaking the âdata down, actions upâ paradigm. With TypeScript, you can annotate both the ref type and the componentâs props for safe, predictable behavior.
Whatâs new in React 19?
Starting in React 19, function components now automatically accept the ref
prop. In many cases you can simply define your component as a function that accesses props.ref
without wrapping it inâŻReact.forwardRef
(and Reactâs upcoming codemods will automatically update legacy code). Still, if you need to customize the value that is exposed (usingâŻuseImperativeHandle
) or if you need compatibility with older React versions, you should continue to useâŻforwardRef
.
forwardRef
?Fundamentally, forwardRef
is a utility that lets you pass a ref from a parent component through a child function component to a specific DOM node or instance. Prior to React 19, function components did not receive a ref
because they lacked an instance. Wrapping a component inâŻReact.forwardRef
provided a way for the parentâs ref to be forwarded to a childâs internal element.
In React 19:
<input>
), you can now simply accept a ref
prop in your function component.useImperativeHandle
), wrapping with forwardRef
is still required.forwardRef
with TypeScriptTraditionally, you would write a function component that accepts a ref as a second parameter using the ForwardRefRenderFunction
type:
1import React, { forwardRef, ForwardRefRenderFunction } from 'react'; 2 3interface ButtonProps { 4 onClick: () => void; 5} 6 7const Button: ForwardRefRenderFunction<HTMLButtonElement, ButtonProps> = ({ onClick }, ref) => ( 8 <button ref={ref} onClick={onClick}> 9 Click Me 10 </button> 11); 12 13export default forwardRef(Button);
In this example the ref is typed as HTMLButtonElement
and the props are typed via ButtonProps
. This pattern ensures that the parent component can interact with the buttonâs DOM node in a typeâsafe way.
Now that function components automatically receive the ref as a prop, if your componentâs only job is to pass the ref along (for example, directly to an <input>
or <button>
), you can write:
1import React from 'react'; 2 3interface TextInputProps { 4 placeholder: string; 5 // Note: the ref will now be included in props automatically. 6} 7 8export function TextInput({ placeholder, ref }: TextInputProps & { ref?: React.Ref<HTMLInputElement> }) { 9 // Directly use the ref prop. 10 return <input ref={ref} placeholder={placeholder} />; 11}
Note: If you need to use hooks such as
useImperativeHandle
to expose custom methods, then you must continue to wrap your component inReact.forwardRef
. In that case the code remains similar to the âold way.â
forwardRef
If you need to expose a childâs DOM node (or a custom handle) to the parent, hereâs an example using forwardRef
with TypeScript:
1import React, { forwardRef, useImperativeHandle, useRef, ChangeEvent } from 'react'; 2 3interface ChildProps { 4 label: string; 5} 6 7const ChildComponent = forwardRef<HTMLInputElement, ChildProps>(({ label }, ref) => { 8 const inputRef = useRef<HTMLInputElement>(null); 9 10 // Expose only the focus method. 11 useImperativeHandle(ref, () => ({ 12 focus: () => { 13 inputRef.current?.focus(); 14 } 15 })); 16 17 return ( 18 <div> 19 <label>{label}</label> 20 <input ref={inputRef} type="text" /> 21 </div> 22 ); 23}); 24 25export default ChildComponent;
In this setup, the parent can now call the focus
method on the childâs input element. This pattern is useful when you want to encapsulate internal behavior while still allowing the parent to control certain actions.
Hereâs how a parent component can use the above child component:
1import React, { useRef } from 'react'; 2import ChildComponent from './ChildComponent'; 3 4const ParentComponent = () => { 5 // Create a ref that expects an object with a focus() method. 6 const inputRef = useRef<{ focus: () => void }>(null); 7 8 const focusInput = () => { 9 inputRef.current?.focus(); 10 }; 11 12 return ( 13 <div> 14 <ChildComponent ref={inputRef} label="Your Name" /> 15 <button onClick={focusInput}>Focus Input</button> 16 </div> 17 ); 18}; 19 20export default ParentComponent;
This parent uses the ref to trigger the focus behavior exposed by the child component.
When more control is needed, you can expose a subset of a child componentâs functionality. For example, using useImperativeHandle
(as shown above) allows you to expose only the methods you choose, keeping internal details private.
forwardRef
When using TypeScript, itâs important to type both the ref and the componentâs props. Hereâs another example:
1import React, { forwardRef } from 'react'; 2 3interface ButtonProps { 4 label: string; 5} 6 7interface ButtonRef { 8 focus: () => void; 9} 10 11const Button = forwardRef<ButtonRef, ButtonProps>(({ label }, ref) => { 12 // In this example, the focus is handled internally. 13 return ( 14 <button ref={ref}> 15 {label} 16 </button> 17 ); 18}); 19 20export default Button;
This ensures that the parentâs ref is correctly typed to only expose the defined methods.
forwardRef
Legacy Support & Custom Imperative Handles:
If you are writing a component library that must work with React versions prior to 19 or you need to expose custom methods (e.g. using useImperativeHandle
), continue using forwardRef
.
Simple DOM Ref Forwarding in React 19:
For components that simply need to pass the ref to a DOM element without modification, you can now take advantage of the new ref-as-a-prop behavior in React 19. This simplifies your code and reduces boilerplate.
Prefer âRef as a Propâ in New Projects:
In React 19 projects, if your component only needs to forward the ref without any transformation, define it as a regular function component and access the ref from props.
Use useImperativeHandle
Only When Necessary:
Only expose methods beyond the raw DOM node if required by your use case.
Keep Ref Usage Focused:
Refs should be used for imperative tasks (like focusing, scrolling, or triggering animations) rather than for driving rendering logic. For stateful logic that affects rendering, use state or context.
With React 19, the landscape of ref handling has changed. For many common cases, you no longer need to wrap your components in forwardRef
just to pass a ref to a DOM nodeâfunction components now receive the ref as a prop automatically. However, if you need to customize what is exposed to the parent (for example, by using useImperativeHandle
), or if you need backward compatibility, using forwardRef
remains the recommended approach.
By following the updated practices above and leveraging TypeScript for strong type safety, you can create robust, reusable components that work seamlessly in both legacy and modern React environments.
Happy coding!