What is the useRef hook in React?

useRef returns a mutable ref object that persists across renders without causing re-renders - used for accessing DOM nodes and storing mutable values.

React

Overview

`useRef` creates a plain mutable container (`{ current: ... }`) that persists across renders. Unlike `useState`, changing `ref.current` does **not** trigger a re-render. It has two main use cases: accessing DOM elements directly, and storing any mutable value you want to "remember" without causing a re-render.

Key Points

  • •`const ref = useRef(initialValue)` - `ref.current` holds the value.
  • •Pass `ref` to a JSX element's `ref` prop to get the DOM node in `ref.current`.
  • •Changing `ref.current` does not cause a re-render.
  • •A ref is like an instance variable in a class component - mutable, persistent, invisible to React.
  • •Common uses: focus management, animations, storing previous values, holding timer IDs.

Examples

Accessing a DOM element

tsx
import { useRef, useEffect } from 'react';

function SearchInput() {
  const inputRef = useRef<HTMLInputElement>(null);

  // Auto-focus on mount
  useEffect(() => {
    inputRef.current?.focus();
  }, []);

  return <input ref={inputRef} placeholder="Search..." />;
}

Store a mutable value without triggering re-renders

tsx
function StopwatchTimer() {
  const [time, setTime] = useState(0);
  const intervalRef = useRef<number | null>(null);

  const start = () => {
    intervalRef.current = setInterval(() => {
      setTime(t => t + 1);
    }, 1000);
  };

  const stop = () => {
    clearInterval(intervalRef.current!);
    intervalRef.current = null;
  };

  return <button onClick={intervalRef.current ? stop : start}>Toggle</button>;
}

The interval ID is stored in a ref - it does not need to live in state because we never render it.

Track the previous value of a state

tsx
function usePrevious<T>(value: T): T | undefined {
  const ref = useRef<T | undefined>(undefined);

  useEffect(() => {
    ref.current = value; // stores current value after render
  });

  return ref.current; // returns previous value (before this render)
}

function Counter() {
  const [count, setCount] = useState(0);
  const prev = usePrevious(count);

  return <p>Now: {count}, Before: {prev}</p>;
}

Pro Tip

If you find yourself reaching for `useRef` to store something that the UI needs to display, you probably want `useState` instead. `useRef` is for values that need to persist across renders but should NOT drive the UI.