The main difference is one line: changing state causes a re-render, and changing a ref does not. State drives the interface, while a ref stores information React does not need to react to. That single rule decides almost every case.
The comparison at a glance
| useRef | useState | |
|---|---|---|
| Returns | An object with a current property | An array with a value and a setter |
| Changing it | No re-render | Triggers a re-render |
| Mutability | Write current directly | Replace through the set function |
| Reading during render | Avoid | Safe, but each render has its own snapshot |
See how useRef works for the full background.
A value the UI must show: use state
A click counter displays its count, so the count belongs in state.
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>Clicked {count} times</button>;
}Every click calls setCount, React renders again with the new number, and the button label updates. The screen stays in sync because the value lives in state.
The same counter with a ref fails
Swapping state for a ref produces a button that never changes its label.
import { useRef } from "react";
function Counter() {
const countRef = useRef(0);
return (
<button onClick={() => (countRef.current += 1)}>
Clicked {countRef.current} times
</button>
);
}The ref updates on every click, but React never re-renders, so the label always shows the initial zero. The value is used for rendering, so a ref is the wrong container.
A value the UI never shows: use a ref
A timer ID is only read back to clear the timer. Nothing about it appears on screen, so it belongs in a ref.
import { useRef } from "react";
function PauseableAlert() {
const timeoutRef = useRef(null);
function handleStart() {
timeoutRef.current = setTimeout(() => console.log("Done"), 3000);
}
function handleCancel() {
clearTimeout(timeoutRef.current);
}The handlers store the timer ID when the timer starts and clear it when it is cancelled. None of this value appears in the JSX, so it never needs a render.
return (
<>
<button onClick={handleStart}>Start</button>
<button onClick={handleCancel}>Cancel</button>
</>
);
}Storing the ID in state would also work, but it would force a re-render that changes nothing visible. The ref keeps the value across renders without that wasted work.
Which should you use?
Use state when the answer to any of these is yes.
- Does the value appear in the JSX?
- Does a change to it affect what renders?
- Does an Effect depend on it?
Use a ref when all of these are true.
- The value is only read or written in event handlers or Effects.
- Changing it should not change the screen.
- The value is a timer ID, DOM node, or similar escape-hatch object.
One false equivalence to avoid
A common mistake is treating a ref as "state without re-rendering" and then reading it during render anyway. That breaks purity and makes output unpredictable.
If you are tempted to read ref.current while rendering, the value belongs in state instead. For values that must be remembered across renders but stay out of the UI, a ref is exactly the right tool.
What to learn next
A ref is also the standard way to keep a copy of the previous value for later comparison. Continue with how to store previous values with useRef.
Rune AI
Key Insights
- useState triggers a re-render; useRef persists without one.
- Put displayed or decision-driving values in state.
- Put timer IDs, DOM nodes, and handler-only values in a ref.
- Read and write refs outside render; read state at any time.
- Never use a ref to hold something the screen must show.
Frequently Asked Questions
Does useRef trigger a re-render?
Can useRef replace useState?
Why is state called immutable?
Conclusion
Use state for any value the UI displays or reacts to, because changing it must re-render the component. Use a ref for values that only event handlers and Effects read, where a re-render would be wasted work.
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.