Fix 'Function components cannot be given refs' with React.forwardRef

intermediateโš›๏ธ React2026-05-08| React 16.3+, any OS, any bundler (Webpack, Vite, CRA, Next.js)

Error Message

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
#react#forwardRef#ref#hooks#javascript#typescript

The Error

You attached a ref to a custom component and got this in the console:

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

Your code probably looks something like this:

function TextInput({ placeholder }) {
  return <input placeholder={placeholder} />;
}

// Somewhere else
const inputRef = useRef(null);
<TextInput ref={inputRef} placeholder="Type here" />

The ref silently does nothing. inputRef.current stays null, React logs that warning, and nothing breaks loudly โ€” which makes this bug easy to miss.

Why This Happens

ref is not a regular prop in React. Write ref={inputRef} on a function component and React intercepts it before it ever reaches your component โ€” it never shows up in props. Since plain function components have no instance, there's nowhere to attach the ref, so React drops it entirely.

Class components sidestep this because they have instances. Function components don't. That's why React.forwardRef() exists โ€” it's an explicit opt-in that tells React: "yes, this component knows how to handle a ref."

The Fix: Wrap with React.forwardRef

Wrap your component with forwardRef. React then passes ref as the second argument to your function โ€” separate from props โ€” and you attach it to whichever DOM element you want to expose.

Before (broken)

function TextInput({ placeholder }) {
  return <input placeholder={placeholder} />;
}

After (fixed)

import { forwardRef } from 'react';

const TextInput = forwardRef(function TextInput({ placeholder }, ref) {
  return <input ref={ref} placeholder={placeholder} />;
});

export default TextInput;

The parent can now reach the underlying <input> DOM node directly:

import { useRef } from 'react';
import TextInput from './TextInput';

function Form() {
  const inputRef = useRef(null);

  function focusInput() {
    inputRef.current?.focus();
  }

  return (
    <>
      <TextInput ref={inputRef} placeholder="Type here" />
      <button onClick={focusInput}>Focus</button>
    </>
  );
}

Using an Arrow Function (also valid)

const TextInput = forwardRef(({ placeholder }, ref) => (
  <input ref={ref} placeholder={placeholder} />
));

TextInput.displayName = 'TextInput';

Always set displayName on arrow-function components. Without it, React DevTools labels the component ForwardRef โ€” fine until you're debugging a component tree with five of them stacked.

Forwarding to a Nested Element

The ref doesn't have to land on the root element. Put it wherever the caller actually needs access:

const Card = forwardRef(function Card({ children, className }, ref) {
  return (
    <div className="card-wrapper">
      <div ref={ref} className={`card ${className}`}>
        {children}
      </div>
    </div>
  );
});

TypeScript: Typing forwardRef Correctly

The generic signature is forwardRef<RefType, PropsType>. Both type parameters matter:

import { forwardRef, InputHTMLAttributes } from 'react';

interface TextInputProps extends InputHTMLAttributes<HTMLInputElement> {
  label?: string;
}

const TextInput = forwardRef<HTMLInputElement, TextInputProps>(
  function TextInput({ label, ...props }, ref) {
    return (
      <div>
        {label && <label>{label}</label>}
        <input ref={ref} {...props} />
      </div>
    );
  }
);

export default TextInput;

Skip the generics and TypeScript infers ref as unknown. That causes type errors wherever the ref is used downstream โ€” often in a completely different file, which makes tracking down the root cause annoying.

Common Mistake: Forgetting to Attach the Ref

This one bites a lot of people. You wrap with forwardRef, the console warning disappears, and everything looks fine. But ref.current is still null:

// BUG: ref argument received but never used
const TextInput = forwardRef(function TextInput({ placeholder }, ref) {
  return <input placeholder={placeholder} />; // ref not attached!
});

Receiving ref as an argument is only half the job. You have to actually pass it to a DOM element or another forwardRef component for anything to work.

Verify the Fix Worked

  • The warning is gone from your browser console.
  • inputRef.current is no longer null after mount. Drop a quick useEffect log to confirm:
useEffect(() => {
  console.log('ref:', inputRef.current); // Should print the DOM element, e.g. <input>
}, []);
  • React DevTools shows your component by name (TextInput, not ForwardRef) โ€” that confirms displayName is set.
  • Imperative calls like .focus() or .scrollIntoView() work without throwing.

Quick Reference

  • Plain function component + ref prop โ†’ warning, ref is null
  • Wrap with forwardRef โ†’ second arg is the ref, attach it to a DOM node
  • TypeScript: use forwardRef<HTMLElement, Props> generics โ€” both matter
  • Set displayName on arrow-function components for readable DevTools output
  • Warning gone but ref still null? You wrapped correctly but forgot to attach the ref inside

Related Error Notes