Design Converter
Education
Last updated on Feb 12, 2025
Last updated on Dec 3, 2024
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!
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.