Understanding useRef in React (Without the Confusion)
Source: Dev.to

If you’ve been learning React, you’ve likely mastered useState and useEffect. But then you run into useRef, and suddenly things feel… a bit weird.
It doesn’t update the screen. It doesn’t trigger re‑renders. It feels like it’s breaking the “React rules.” The secret is that useRef is your component’s private, persistent storage.
Let’s break down what it actually does, why it’s useful, and how to use it without the headache.
What useRef Actually Is
At its core, useRef gives you a place to store a value that:
- stays the same between renders
- can be changed anytime
- does not cause a re‑render when it changes
const myRef = useRef(initialValue);
React gives you an object that looks like this:
{ current: initialValue }
The important part is .current. That’s where the value lives. React creates this object once and keeps returning the same one on every render.
Why useRef Exists at All
You might wonder: “Why not just use useState for everything?” The difference comes down to visibility.
- State is for things you want the user to see (UI). When state changes, React re‑paints the screen.
- Refs are for things you want to remember behind the scenes. When a ref changes, React stays silent and does nothing to the UI.
Typical ref use‑cases:
- a DOM element
- a timer ID
- a WebSocket connection
- a previous value
- a flag or counter
Putting these in state would cause unnecessary re‑renders. Using normal variables would reset them on every render. useRef solves this exact problem by giving you memory without triggering renders.
The Most Common Use: Accessing the DOM
This is where most people meet useRef first.
import { useRef } from "react";
function MyComponent() {
const inputRef = useRef(null);
return (
<>
inputRef.current.focus()}>
Focus
);
}
React puts the real DOM input element into inputRef.current, allowing you to call methods like focus() directly. This is the correct way to:
- focus inputs
- scroll elements into view
- measure width or height
- control video or audio elements
DOM nodes don’t belong in state; refs are the right tool.
Keeping Values Between Renders (Without Re‑rendering)
Sometimes you need to remember something, but you don’t want React involved. Example: counting how many times a component renders.
const renderCount = useRef(0);
renderCount.current += 1;
This value:
- increases on every render
- never causes a re‑render
- never resets
Useful for counters, flags, cached data, or internal bookkeeping.
Tracking the Previous Value
React doesn’t give you “previous state” by default. useRef fills that gap.
function MyComponent({ value }) {
const prevValue = useRef(value);
useEffect(() => {
prevValue.current = value;
}, [value]);
// now prevValue.current holds the previous `value`
}
value is the current value, while prevValue.current is the previous one. This pattern is clean, predictable, and widely used.
Storing External Objects
Some things don’t belong to React at all:
- WebSocket connections
AbortControllerinstances- observers
- third‑party library objects
function useWebSocket(url) {
const socketRef = useRef(null);
useEffect(() => {
socketRef.current = new WebSocket(url);
return () => socketRef.current.close();
}, [url]);
return socketRef.current;
}
These objects need a stable place to live; useRef provides it. Putting them in state would be a design mistake.
What useRef Is Not
- It is not a replacement for
useState. - It is not reactive and should not be used to update the UI.
If the UI depends on a value, use state. If React doesn’t need to know about it, use a ref. Following this rule prevents most mistakes.
A Simple Mental Model
Think of useRef like a small box attached to your component:
- React renders the component.
- The box stays the same across renders.
- You can put anything inside the box (
.current). - React ignores the box completely.
That’s the deal.
Final Thoughts
useRef isn’t complicated—it’s just honest. It gives you a place to store things React shouldn’t manage. Once you understand that, it stops feeling “advanced” and starts feeling obvious. When it finally clicks, your components become:
- cleaner
- more predictable
- easier to reason about
That’s not magic—that’s using the right tool for the right job.