What is the `useRef` hook in React and when should it be used?
TL;DR
The useRef hook in React is used to create a mutable object that persists across renders. It can be used to access and manipulate DOM elements directly, store mutable values that do not cause re-renders when updated, and keep a reference to a value without triggering a re-render. For example, you can use useRef to focus an input element:
import { useEffect, useRef } from 'react';function TextInputWithFocusButton() {const inputEl = useRef(null);useEffect(() => {inputEl.current?.focus();}, []);return <input ref={inputEl} type="text" />;}
What is the useRef hook in React and when should it be used?
useRef preserves a mutable container across renders for values that should not themselves trigger rendering.
Introduction to useRef
The useRef hook in React is a function that returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component. Updating ref.current does not trigger a re-render.
A few important rules:
- Avoid reading or writing
ref.currentduring rendering because it makes output depend on mutable data React does not track. The documented exception is predictable one-time initialization, such as filling anullref with a lazily created object. - It is fine (and expected) to read or write
ref.currentinside event handlers, effects, or callbacks.
Key use cases for useRef
Refs primarily bridge to imperative host objects or retain non-rendered mutable values between component calls.
Accessing and manipulating DOM elements
One of the primary use cases for useRef is to directly access and manipulate DOM elements. This is particularly useful when you need to interact with the DOM in ways that are not easily achievable through React's declarative approach.
Example:
import { useEffect, useRef } from 'react';function TextInputWithFocusButton() {const inputEl = useRef(null);useEffect(() => {inputEl.current?.focus();}, []);return <input ref={inputEl} type="text" />;}
In this example, useRef holds the input DOM node and the Effect focuses it after the node has been committed. Optional chaining handles the case where the ref is not attached.
Storing mutable values across renders
useRef can also be used to store any mutable value that should persist across renders without causing one. Common examples are interval/timeout IDs, the previous value of a prop or state, an instance of a non-React object (e.g. a chart or map controller), or a counter used inside event handlers.
import { useEffect, useRef, useState } from 'react';function Stopwatch() {const [seconds, setSeconds] = useState(0);const intervalRef = useRef(null);function start() {if (intervalRef.current !== null) return;intervalRef.current = window.setInterval(() => {setSeconds((value) => value + 1);}, 1000);}function stop() {window.clearInterval(intervalRef.current);intervalRef.current = null;}useEffect(() => {return () => window.clearInterval(intervalRef.current);}, []);return (<div><p>{seconds} seconds</p><button onClick={start}>Start</button><button onClick={stop}>Stop</button></div>);}
Here the interval ID must survive renders but does not belong in the UI, so a ref is appropriate. The displayed elapsed time remains state because changing it must trigger a render.
Refs in React 19
React 19 changed how refs interoperate with components in two important ways:
ref is now a regular prop — forwardRef is no longer required
In function components, ref is now an ordinary prop. You can accept it directly in your props and pass it to a DOM node (or to another component) without wrapping the component in React.forwardRef. forwardRef still works but is no longer necessary, and React plans to deprecate, then remove, it in a future major.
// React 19+: just accept `ref` as a prop.function FancyInput({ ref, ...props }) {return <input ref={ref} {...props} />;}// Usage is unchanged at the call site:function Parent() {const inputRef = useRef(null);return <FancyInput ref={inputRef} placeholder="Type..." />;}
Cleanup functions from ref callbacks
Ref callbacks may now return a cleanup function, which React runs when the ref detaches (similar to useEffect). This removes the need for the older "called with null" pattern.
import { useCallback } from 'react';function reportFocus(event) {console.log('Focused:', event.currentTarget.name);}function FocusableInput() {const inputRef = useCallback((node) => {node.addEventListener('focus', reportFocus);return () => node.removeEventListener('focus', reportFocus);}, []);return <input ref={inputRef} name="email" />;}