useRef
useRef gives a component a box that survives re-renders — for reaching a DOM element, or for remembering a value without causing a re-render when it changes.
On this page · 5 min · Intermediate
On this page
- Learning objectives
- Explanation
- Syntax
- Code example
- Step by step
- Real-world use
- Common mistakes
- Best practices
- Remember this
- Practice exercise
- Mini quiz
- Related topics
- My notes
- Reading time
- 5 min
- Difficulty
- Intermediate
- Updated
- Oct 28, 2026
Related tutorials
Learning objectives#
- Access DOM nodes with ref={myRef}
- Focus, scroll and measure elements
- Store mutable values such as timer ids
- Know when to use a ref instead of state
Explanation#
const inputRef = useRef(null); then <input ref={inputRef} />. After rendering, inputRef.current is the DOM element: inputRef.current.focus().
A ref is a plain object { current: ... } that React keeps for the component’s lifetime. Changing ref.current does NOT re-render.
Use refs for values that matter to your code but not to what is displayed: timer ids, the previous value, whether a request is still current.
If the screen must change when a value changes, use state, not a ref.
Read and write refs in event handlers and effects, not during rendering. In React 19, ref is a normal prop, so your own components can accept ref directly.
Syntax#
const inputRef = useRef(null);
<input ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>Focus</button>
const timerId = useRef(null);
timerId.current = setInterval(tick, 1000);
Code example: A stopwatch and an auto-focusing search#
import { useEffect, useRef, useState } from 'react';
export default function Stopwatch() {
const [elapsed, setElapsed] = useState(0); // shown on screen → state
const timerId = useRef(null); // not shown → ref
const searchRef = useRef(null);
const renders = useRef(0);
renders.current += 1; // counting renders does not cause one
useEffect(() => {
searchRef.current.focus(); // DOM access after mount
return () => clearInterval(timerId.current); // clean up on unmount
}, []);
function start() {
if (timerId.current) return; // already running
timerId.current = setInterval(() => setElapsed(s => s + 1), 1000);
}
function stop() {
clearInterval(timerId.current);
timerId.current = null;
}
return (
<>
<input ref={searchRef} placeholder="I am focused on load" aria-label="Search" />
<p>{elapsed} s</p>
<button onClick={start}>Start</button>
<button onClick={stop}>Stop</button>
<button onClick={() => { stop(); setElapsed(0); }}>Reset</button>
<small> (rendered {renders.current} times)</small>
</>
);
}
The search box is focused as soon as the component appears. Start counts seconds; Stop pauses; Reset sets 0 s. Clicking Start twice does not create two timers, because the running timer’s id is kept in a ref.
This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.
Step-by-step explanation#
- elapsed is state because it is displayed.
- timerId is a ref: the id is needed by stop() but changing it should not re-render.
- searchRef.current is the input element, focused in an effect after mount.
- The effect’s cleanup clears the interval if the component disappears.
Real-world use case#
A chat window keeps a ref to the message list and scrolls it to the bottom whenever a new message arrives; a video player keeps a ref to the <video> element to call play() and pause().
Common mistakes#
Changing ref.current does not re-render, so the screen shows stale values. Use state for anything rendered.
During the first render the element does not exist yet (current is null). Access it in effects or handlers.
let id = setInterval(...) is lost on the next render. Keep it in a ref.
Best practices#
- State for what users see, refs for what code needs
- Access DOM refs in effects or handlers
- Clean up timers stored in refs
- Prefer declarative props over imperative DOM calls when possible
Remember this#
ref = box that survives renders; changing it does not re-render.
ref={myRef} → myRef.current is the DOM element.
Timer ids, previous values, DOM nodes → refs.
Practice exercise#
Your task: Build a "Copy code" box: a readonly input with a coupon code and a button that selects the input’s text using a ref and shows "Copied!".
Need a hint?
Show solution
import { useRef, useState } from 'react';
export default function CouponCopy() {
const inputRef = useRef(null);
const [copied, setCopied] = useState(false);
function copy() {
inputRef.current.select();
navigator.clipboard?.writeText(inputRef.current.value);
setCopied(true);
}
return (
<>
<input ref={inputRef} readOnly value="SAVE10" aria-label="Coupon code" />
<button onClick={copy}>Copy</button>
{copied && <span> Copied!</span>}
</>
);
}
Mini quiz#
What happens when you change ref.current?
Want more? Practise all React MCQs · React interview questions
My notes#
Saved in this browser.