React

What is forwardRef() in React?

The short answer

forwardRef is a React API that lets a component receive a ref from its parent and forward it to a DOM element (or another component) inside it. By default, refs don't pass through components — if you put a ref on a custom component, it won't magically reach the inner <input> or <div>. forwardRef bridges that gap.

What refs are, quickly

A ref is a way to get direct access to a DOM element. You create one with useRef, attach it to an element, and React populates it with the actual DOM node after rendering:

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

const handleClick = () => {
inputRef.current.focus();
};

return (
<>
<input ref={inputRef} placeholder="Search..." />
<button onClick={handleClick}>Focus input</button>
</>
);
}

This works perfectly when you're attaching the ref to a native HTML element. The problem shows up when you put a layer of abstraction in between.

The problem: refs don't pass through components

Let's say you've built a reusable TextInput component:

function TextInput({ placeholder }) {
return (
<input
placeholder={placeholder}
className="text-input"
/>
);
}

Now you try to attach a ref to it from a parent:

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

return (
<>
{/* This ref won't reach the <input> inside TextInput */}
<TextInput ref={inputRef} placeholder="Search..." />
<button onClick={() => inputRef.current.focus()}>
Focus
</button>
</>
);
}

This doesn't work. ref is not a regular prop — React handles it specially. By default, function components don't receive ref at all. The ref just doesn't go anywhere, and inputRef.current stays null.

How forwardRef solves it

forwardRef wraps your component and gives it access to the ref that the parent passed in. You then attach that ref to whatever DOM element makes sense:

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

Now the parent's ref reaches the actual <input> element inside TextInput:

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

return (
<>
<TextInput ref={inputRef} placeholder="Search..." />
<button onClick={() => inputRef.current.focus()}>
Focus
</button>
</>
);
}

The ref flows through the custom component and attaches to the inner <input>. The parent can now call .focus(), measure dimensions, or do anything else you'd normally do with a DOM ref.

When you need it

The most common scenarios:

Building reusable component libraries. If you're creating a Button, Input, Modal, or any component that wraps a DOM element, consumers will eventually need ref access. A design system without forwardRef is frustrating to use.

Focus management. Forms that auto-focus the next field, search inputs that focus on keyboard shortcut, modals that trap focus — all of these need refs to the actual DOM elements.

Integrating with third-party libraries. Some libraries (animation libraries, measurement utilities, drag-and-drop) need direct DOM access. If your components don't forward refs, these integrations break.

Imperative animations. When you need to control scroll position, trigger CSS transitions, or measure element sizes, you need the real DOM node.

Combining with useImperativeHandle

Sometimes you don't want to expose the entire DOM element to the parent. Maybe your component is complex and you only want the parent to call specific methods. useImperativeHandle lets you customize what the ref exposes:

const VideoPlayer = forwardRef(function VideoPlayer(
{ src },
ref
) {
const videoRef = useRef(null);

useImperativeHandle(ref, () => ({
play() {
videoRef.current.play();
},
pause() {
videoRef.current.pause();
},
restart() {
videoRef.current.currentTime = 0;
videoRef.current.play();
},
}));

return <video ref={videoRef} src={src} />;
});

Now the parent gets an object with play, pause, and restart — not the raw video element. This is a cleaner API that hides implementation details:

function App() {
const playerRef = useRef(null);

return (
<>
<VideoPlayer ref={playerRef} src="/video.mp4" />
<button onClick={() => playerRef.current.play()}>
Play
</button>
<button onClick={() => playerRef.current.pause()}>
Pause
</button>
<button onClick={() => playerRef.current.restart()}>
Restart
</button>
</>
);
}

Forwarding refs through multiple layers

If your component wraps another component that also uses forwardRef, the ref chains through naturally:

const FancyInput = forwardRef(
function FancyInput(props, ref) {
return (
<div className="fancy-wrapper">
<TextInput ref={ref} {...props} />
</div>
);
}
);

As long as each component in the chain uses forwardRef, the ref reaches the final DOM element regardless of how many layers deep it is.

A note on React 19 and beyond

In newer versions of React, function components can accept ref as a regular prop without needing forwardRef. This simplifies the API significantly. But forwardRef is still widely used in existing codebases and libraries, so understanding it remains important.

Why interviewers ask this

This question checks whether you understand React's ref system beyond the basics. Knowing that refs don't automatically pass through components — and knowing the mechanism to fix that — shows you've built real, composable component abstractions. It also opens the door to follow-up questions about useImperativeHandle, when imperative code is appropriate in React, and how to design component APIs that are both flexible and well-encapsulated.