What is the useRef hook in React?

React

The short answer

useRef gives you a mutable container that holds a value across renders without causing a re-render when it changes. It is commonly used for two things: accessing DOM elements directly and storing values that need to persist between renders but do not affect the UI.

Accessing DOM elements

The most common use of useRef is to get a reference to a DOM element:

function TextInput() {
const inputRef = useRef(null);
const handleClick = () => {
inputRef.current.focus();
};
return (
<>
<input ref={inputRef} type="text" />
<button onClick={handleClick}>Focus Input</button>
</>
);
}

When you pass inputRef to the ref prop, React sets inputRef.current to the actual DOM element after the component mounts. Now you can call DOM methods like .focus(), .scrollIntoView(), or read properties like .offsetHeight directly.

Storing values without re-rendering

This is the second important use. Sometimes you need a value to persist between renders, but changing it should not trigger a re-render. State causes re-renders — refs do not.

function StopWatch() {
const [time, setTime] = useState(0);
const intervalRef = useRef(null);
const start = () => {
intervalRef.current = setInterval(() => {
setTime((t) => t + 1);
}, 1000);
};
const stop = () => {
clearInterval(intervalRef.current);
};
return (
<div>
<p>{time} seconds</p>
<button onClick={start}>Start</button>
<button onClick={stop}>Stop</button>
</div>
);
}

We store the interval ID in a ref because we need it later to clear the interval, but we do not want to re-render the component every time we set it.

useRef vs useState

The key difference is simple:

  • useState: Changing the value triggers a re-render. Use it for data that the UI displays.
  • useRef: Changing the value does not trigger a re-render. Use it for data that the UI does not need to display.
function Counter() {
const [count, setCount] = useState(0); // re-renders on change
const renderCount = useRef(0); // does NOT re-render on change
renderCount.current += 1;
return (
<div>
<p>Count: {count}</p>
<p>Renders: {renderCount.current}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}

Storing previous values

A common pattern is using useRef to keep track of the previous value of a prop or state:

function PriceDisplay({ price }) {
const prevPrice = useRef(price);
useEffect(() => {
prevPrice.current = price;
}, [price]);
return (
<div>
<p>Current: ${price}</p>
<p>Previous: ${prevPrice.current}</p>
</div>
);
}

The ref stores the previous price. After each render, the useEffect updates it to the current price.

Common Pitfalls

A common mistake is trying to use useRef to store a value and then expecting the component to update when that value changes. If you need the UI to reflect a value, use useState. Refs are invisible to React's rendering — changing .current does nothing to the UI.

Interview Tip

When explaining useRef, cover both use cases: DOM access and persistent values. Show that you know the difference between useRef and useState. If the interviewer asks when you would use a ref instead of state, the answer is: when you need a value that persists across renders but should not cause a re-render when it changes. Timer IDs, previous values, and DOM references are the classic examples.

Why interviewers ask this

useRef is one of the core React hooks, and many candidates do not fully understand when to use it. Interviewers want to see if you know both use cases (DOM access and persistent values), if you understand why it does not cause re-renders, and if you can choose between useRef and useState correctly.