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:

  1. Direct access and manipulation of DOM elements.
  2. 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:

  1. Initialize the ref: Call useRef(null) inside your component.
  2. Attach the ref: Pass the ref object to the ref attribute of the target JSX element.
  3. Access the node: Use the .current property of the ref to access and manipulate the actual DOM element (usually inside a useEffect hook 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