How to Use useRef Hook in React
This article provides a practical guide on how to implement the
useRef hook in React. You will learn the core concepts of
useRef, understand how it differs from standard state
management, and see step-by-step examples of how to use it for accessing
DOM elements and persisting mutable values across renders without
triggering a component re-render.
What is the useRef Hook?
The useRef hook is a built-in React hook that returns a
mutable object with a single .current property. Unlike
state variables managed with useState, updating the
.current property of a ref does not trigger a component
re-render. This makes it ideal for two main use cases:
- Direct access and manipulation of DOM elements.
- Storing mutable values that need to persist across renders without causing UI updates.
To use useRef, you must first import it from React:
import { useRef } from 'react';Use Case 1: Accessing and Manipulating DOM Elements
The most common use case for useRef is to interact with
DOM nodes directly (e.g., focusing an input, playing media, or measuring
element dimensions).
Step-by-Step Implementation:
- Initialize the ref: Call
useRef(null)inside your component. - Attach the ref: Pass the ref object to the
refattribute of the target JSX element. - Access the node: Use the
.currentproperty of the ref to access and manipulate the actual DOM element (usually inside auseEffecthook or an event handler).
Example: Auto-focusing an Input Element
import React, { useRef } from 'react';
function FocusInput() {
// 1. Initialize the ref with null
const inputRef = useRef(null);
const handleFocus = () => {
// 3. Access the DOM node and call focus()
inputRef.current.focus();
};
return (
<div>
{/* 2. Attach the ref to the input element */}
<input ref={inputRef} type="text" placeholder="Click button to focus..." />
<button onClick={handleFocus}>Focus Input</button>
</div>
);
}
export default FocusInput;Use Case 2: Persisting Values Across Renders (Without Re-rendering)
If you need to keep track of a value that changes over time, but you
do not want those changes to force the component to redraw,
useRef is the correct tool.
If you used useState for this, every update would
trigger a visual re-render. With useRef, the value updates
instantly in the background.
Example: Creating a Stopwatch Interval
import React, { useState, useRef } from 'react';
function Timer() {
const [seconds, setSeconds] = useState(0);
// Store the interval ID in a ref so it persists across renders
const timerRef = useRef(null);
const startTimer = () => {
if (timerRef.current !== null) return;
timerRef.current = setInterval(() => {
setSeconds((prev) => prev + 1);
}, 1000);
};
const stopTimer = () => {
clearInterval(timerRef.current);
timerRef.current = null; // Reset the ref
};
return (
<div>
<h1>Seconds: {seconds}</h1>
<button onClick={startTimer}>Start</button>
<button onClick={stopTimer}>Stop</button>
</div>
);
}
export default Timer;Key Rules to Remember
- Do not use refs during rendering: Avoid reading or
writing
ref.currentdirectly in the body of your component function during the render phase. This can lead to unpredictable behavior. Only read or write refs inside event handlers oruseEffecthooks. - Refs are synchronous: Unlike state variables, which
are updated asynchronously, mutations to
.currenthappen immediately.