useRef

react

useRef() is an escape hatch that provides a mutable value container persisting across renders. Unlike state, updating a ref doesn’t trigger re-renders. Refs should not be read or written during the render phase (component function body or JSX).

useRef() documentation | Referencing Values with Refs | Manipulating the DOM with Refs

Storing arbitrary values

useRef() can hold any value, not just DOM elements. Use it for storing mutable values that shouldn’t trigger re-renders:

const intervalId = useRef(null);

useEffect(() => {
    intervalId.current = setInterval(() => {
        console.log('Tick');
    }, 1000);

    return () => clearInterval(intervalId.current);
}, []);

This is beneficial for values that need to persist across renders without causing re-renders (timers, subscriptions, previous values).

Storing DOM elements

useRef() is commonly used to reference DOM elements directly. DOM refs are initialized and updated after React commits to the DOM:

const inputRef = useRef(null);

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

return <input ref={inputRef} />;

Callback refs can be used for more control over ref lifecycle:

<input
    ref={(node) => {
        if (node) {
            node.focus();
        }
    }}
/>

Passing ref to child components

In React 19, refs can be passed directly as props without forwardRef:

function CustomInput({ ref }) {
    return <input ref={ref} />;
}

// Usage
const inputRef = useRef(null);
<CustomInput ref={inputRef} />;

useImperativeHandle customizes the ref value exposed to parent components:

useImperativeHandle documentation

function CustomInput({ ref }) {
    const inputRef = useRef(null);

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

    return <input ref={inputRef} />;
}

// Parent can call: ref.current.focus() or ref.current.clear()