Refs as Props

A ref put on your own component used to go nowhere. ref was not a prop — React 7,897 intercepted it, and a function component had to be wrapped in forwardRef to receive it as a second argument. React 19 removed the special case: ref is an ordinary prop, so a component can destructure it and pass it on.

A reusable input that forwards its ref, React 19 styleJavaScript
function MyInput({ label, ref, ...rest }) {
  return <label>{label} <input ref={ref} {...rest} /></label>;
}
function SignupForm() {
  const emailRef = useRef(null);
  return <form onSubmit={e => { e.preventDefault(); emailRef.current.focus(); }}>
    <MyInput label="Email" ref={emailRef} type="email" />
    <button>Sign up</button></form>;
}

SignupForm never learns which DOM node it got; MyInput decides. That is the point — the child publishes one node as its public handle and can change its markup freely, as long as the ref keeps landing on something with the methods the parent calls.

Three consequences follow. A component that does not declare ref ignores it the way it ignores any unused prop, so there is no warning and no node: forwarding is opt-in. ref spread through {...rest} works, since it is now part of props. And element.ref is deprecated in favor of element.props.ref.

forwardRef still works in React 19 and is what you will meet in older code and in libraries that also support React 18. The official reference calls it unnecessary and says it will be deprecated in a future release.

The same component written for React 18 with forwardRefJavaScript
const MyInput = forwardRef(function MyInput({ label, ...rest }, ref) {
  return <label>{label} <input ref={ref} {...rest} /></label>;
});

Class components remain the exception: a ref on a class yields the instance, with no forwarding involved. That is a legacy path (Class Components as Legacy); the modern way to choose what a parent may call is useImperativeHandle (useImperativeHandle).