A ref created in a parent component stays null until a child hands it to a real DOM node. Passing refs between components means accepting ref as a prop and forwarding it down. In current React, this works without any wrapper function. The child decides which node the ref points at.
Pass ref as a prop
In React 19 and later, ref is an ordinary prop on function components. The child receives it like any other prop and attaches it to the element it wants to expose. The parent creates the ref with useRef and passes it down just like a value prop.
function MyInput({ ref }) {
return <input ref={ref} />;
}The child simply forwards the ref prop to the real input. Nothing else is needed, because ref is handled as a normal prop in current React.
import { useRef } from "react";
function Form() {
const inputRef = useRef(null);
return (
<>
<MyInput ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>Focus</button>
</>
);
}Clicking the button focuses the input. The parent's inputRef points at the input DOM node after commit, while the child exposes nothing else. On the first render the ref is null, because the node does not exist until React commits the output.
Forward through multiple components
A ref can travel through several layers. Each intermediate component just passes the same ref down.
function FormField({ ref }) {
return <MyInput ref={ref} />;
}
function Form() {
const inputRef = useRef(null);
return (
<>
<FormField ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>Focus</button>
</>
);
}FormField hands the ref to MyInput, and MyInput hands it to the input element. The parent still ends up with the DOM node no matter how many layers sit in between. Intermediate components do not need to know what the node is, they only pass the ref along.
The older forwardRef pattern
Before React 19, function components could not receive ref as a prop, so they wrapped themselves in forwardRef.
import { forwardRef } from "react";
const MyInput = forwardRef(function MyInput(props, ref) {
return <input ref={ref} />;
});forwardRef still works in React 19, but the React docs mark it for deprecation in a future release and recommend the ref-as-a-prop form for new code. Older codebases and libraries still rely on forwardRef. The ref arrives as the second argument to the render function, after props.
When not to expose a ref
Exposing a DOM node lets the parent reach into the child's internals, which makes the child harder to change later. Prefer exposing a ref from reusable low-level components like buttons and inputs. A reusable Button might expose focus, while a whole Comment component should stay closed.
For app-level components, pass behavior as props instead. When you must expose a limited API rather than a whole node, use useImperativeHandle.
What to learn next
For a controlled, constrained API instead of a raw DOM node, continue with callback refs vs object refs.
Rune AI
Key Insights
- In React 19, ref is an ordinary prop on function components.
- A child forwards ref by attaching it to a DOM node.
- Refs can pass through multiple component layers.
- forwardRef still works today but the docs mark it for deprecation in a future release.
- Expose refs from low-level components, and prefer props for behavior.
Frequently Asked Questions
Do I still need forwardRef in React 19?
Why is my ref null after passing it to a child?
Can a ref pass through multiple components?
Conclusion
Passing a ref between components means accepting ref as a prop and forwarding it to the DOM node you want to expose. React 19 made this a plain prop, so new code skips forwardRef.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.