React useRef Hook

Access DOM nodes directly and persist mutable values across renders without triggering re-renders with useRef.

TL;DR

  1. Hold persistent mutable values across render cycles using the useRef hook.
  2. Access underlying DOM elements directly by assigning references to the ref property.
  3. Store background timer identifiers without triggering expensive component re-renders.

DOM Node Access

    Focus Input Element

    Capture input element reference and trigger focus imperatively.

    const inputRef = useRef<HTMLInputElement>(null);
    const onFocus = () => inputRef.current?.focus();
    return <InputBase ref={inputRef} />;
    Scroll to View

    Scroll container smoothly into view using ref target.

    const boxRef = useRef<HTMLElement>(null);
    const scroll = () => {
      boxRef.current?.scrollIntoView({
        behavior: 'smooth',
      });
    };
    Measure DOM Node

    Read bounding dimensions of rendered DOM elements.

    const boxRef = useRef<HTMLDivElement>(null);
    const getWidth = () => {
      return boxRef.current?.offsetWidth;
    };

Storing Mutable Values

    Timer ID Storage

    Retain interval ID across renders without causing UI updates.

    const timerRef = useRef<number | null>(null);
    const start = () => {
      timerRef.current = window.setInterval(tick, 1000);
    };
    const stop = () => {
      if (timerRef.current) {
        clearInterval(timerRef.current);
      }
    };
    Render Counter

    Track the number of times a component renders.

    const renderCount = useRef(0);
    useEffect(() => {
      renderCount.current += 1;
    });
    Previous Value Tracker

    Store prior prop or state values to detect changes.

    const prevValRef = useRef(val);
    useEffect(() => {
      prevValRef.current = val;
    }, [val]);

Ref Forwarding in React 19

    Standard Ref Prop

    In React 19, function components accept ref as prop.

    export function CustomInput({
      ref,
      ...props
    }: { ref?: React.Ref<HTMLInputElement> }) {
      return <InputBase ref={ref} {...props} />;
    }
    Callback Ref

    Pass callback function to execute code on attach/detach.

    const refCallback = (node: HTMLElement | null) => {
      if (node) console.log('Mounted:', node);
    };
    return <CardView ref={refCallback} />;
    Imperative Handle

    Expose custom methods to parents using useImperativeHandle.

    useImperativeHandle(ref, () => ({
      reset: () => {
        if (inputRef.current) inputRef.current.value = '';
      },
    }));

Common Ref Antipatterns

    Render-Time Mutation

    Mutating ref during render breaks purity and concurrent React.

    // BAD: Mutating ref in render body
    ref.current = computeValue();
    Overusing Refs for State

    Using ref instead of state leaves UI out of sync.

    // BAD: UI will not update
    ref.current = 'New Title';
    Missing Null Guard

    Failing to check if current is defined before invocation.

    // SAFE: Optional chaining
    ref.current?.focus();

Tips

  1. Mutating the ref.current property never causes a component re-render, making it ideal for tracking background timers.
  2. Always check that ref.current is not null before invoking native DOM methods like focus or scroll.

Warnings

  1. Do not read or write ref.current during rendering because React expects component render functions to remain pure.
  2. Avoid using useRef as a substitute for useState when values directly determine what appears in JSX markup.

In Practice

FAQ