In React 19, you can pass ref as a regular prop to function components — no need for React.forwardRef. This simplifies component APIs significantly.
Before React 18 — forwardRef required:
1import { forwardRef, useRef } from "react";23const MyInput = forwardRef<HTMLInputElement, { placeholder?: string }>(4 ({ placeholder }, ref) => {5 return <input ref={ref} placeholder={placeholder} />;6 }7);89function App() {10 const inputRef = useRef<HTMLInputElement>(null);11 return <MyInput ref={inputRef} placeholder="Type..." />;12}
After React 19 — ref as prop:
1import { useRef, type Ref } from "react";23interface MyInputProps {4 placeholder?: string;5 ref?: Ref<HTMLInputElement>;6}78function MyInput({ placeholder, ref }: MyInputProps) {9 return <input ref={ref} placeholder={placeholder} />;10}1112function App() {13 const inputRef = useRef<HTMLInputElement>(null);14 return <MyInput ref={inputRef} placeholder="Type..." />;15}
Benefits:
forwardRef wrapper neededRef callbacks with cleanup (React 19): React 19 also supports cleanup functions in ref callbacks:
1function TrackedElement({ ref }: { ref?: Ref<HTMLDivElement> }) {2 return (3 <div4 ref={(node) => {5 if (node) {6 // Setup: start observing7 const observer = new ResizeObserver(handleResize);8 observer.observe(node);910 // Cleanup (React 19): stop observing11 return () => observer.disconnect();12 }13 }}14 >15 Content16 </div>17 );18}
Forwarding ref to multiple children: With the ref-as-prop pattern, you can forward a ref to any element, or even split it across multiple elements:
1function DualInput({2 label,3 firstRef,4 secondRef,5}: {6 label: string;7 firstRef?: Ref<HTMLInputElement>;8 secondRef?: Ref<HTMLInputElement>;9}) {10 return (11 <div>12 <label>{label} (First)</label>13 <input ref={firstRef} />14 <label>{label} (Second)</label>15 <input ref={secondRef} />16 </div>17 );18}
Migration note: forwardRef still works in React 19 — it's not deprecated. But new components should prefer the ref-as-prop pattern for simplicity.