Refs

useRef

Creates a mutable ref object that persists across renders without triggering re-renders when its value changes. Commonly used for DOM access and storing mutable instance variables.

Signature

TypeScript
const ref = useRef<T>(initialValue: T)

Parameters

ParameterTypeDescription
initialValueTThe initial value assigned to ref.current. For DOM refs, typically pass null and type as useRef<HTMLElement>(null).

Return Value

A ref object with a single mutable property: { current: T }. The same object is returned on every render.

Examples

DOM Element Access
import { useRef, useEffect } from 'react';

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

  useEffect(() => {
    inputRef.current?.focus();
  }, []);

  return <input ref={inputRef} placeholder="I auto-focus on mount" />;
}
Previous Value Tracking
import { useRef, useEffect, useState } from 'react';

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

  useEffect(() => {
    ref.current = value;
  }, [value]);

  return ref.current;
}

function PriceDisplay({ price }: { price: number }) {
  const prevPrice = usePrevious(price);
  const direction = prevPrice !== undefined
    ? price > prevPrice ? '↑' : price < prevPrice ? '↓' : '→'
    : '→';

  return <p>{direction} ${price.toFixed(2)}</p>;
}
Interval Management
import { useRef, useState, useEffect, useCallback } from 'react';

function Stopwatch() {
  const [elapsed, setElapsed] = useState(0);
  const [running, setRunning] = useState(false);
  const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);

  useEffect(() => {
    if (running) {
      intervalRef.current = setInterval(() => {
        setElapsed(e => e + 10);
      }, 10);
    }
    return () => {
      if (intervalRef.current) clearInterval(intervalRef.current);
    };
  }, [running]);

  const reset = useCallback(() => {
    setRunning(false);
    setElapsed(0);
  }, []);

  const seconds = (elapsed / 1000).toFixed(2);

  return (
    <div>
      <p>{seconds}s</p>
      <button onClick={() => setRunning(r => !r)}>
        {running ? 'Pause' : 'Start'}
      </button>
      <button onClick={reset}>Reset</button>
    </div>
  );
}

Common Pitfalls

!

Reading or writing ref.current during render — refs should only be accessed in event handlers, effects, or callbacks, not in the render body.

!

Using useRef when you actually need state — changes to ref.current don't trigger re-renders, so the UI won't update.

!

Forgetting to initialize DOM refs with null, which can cause TypeScript errors.

!

Storing a callback in a ref without updating it, leading to stale function references.

Understanding useRef

useRef serves two primary purposes in React: accessing DOM elements and storing mutable values that persist across renders without triggering re-renders. Unlike state, mutating ref.current does not schedule a re-render, making refs ideal for storing values that change frequently but don't affect the visual output, such as timer IDs, previous prop values, or WebSocket instances.

For DOM access, you pass the ref object to a JSX element's ref attribute. React assigns the DOM node to ref.current after mounting and sets it back to null when the element unmounts. This gives you direct access to DOM APIs like focus(), scrollIntoView(), or getBoundingClientRect() — operations that React's declarative model doesn't directly expose.

A powerful pattern is using refs to store the latest version of a callback or value without re-running effects. For example, if you have an interval that calls a callback, storing the callback in a ref and reading ref.current inside the interval means you always call the latest version without needing to restart the interval when the callback changes. This "ref callback" pattern is especially useful for event handlers in effects.

The ref object returned by useRef is guaranteed to be the same object instance across the entire lifetime of the component. This stability is what makes it different from a plain object stored in useState — React returns the exact same { current } object every render. This makes refs safe to include in dependency arrays (they'll never cause re-runs) and efficient to pass to child components.

Related Hooks

More Refs Hooks

Explore All React Hooks

Browse our complete reference of 19 React hooks with signatures, examples, pitfalls, and in-depth explanations.