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 React, { useRef, useEffect } 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?
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:
- Do not read or write
ref.currentduring rendering. React only guarantees the ref's value is settled after commit; mutating it during render makes components impure and is disallowed. - It is fine (and expected) to read or write
ref.currentinside event handlers, effects, or callbacks.
Key use cases for useRef
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 React, { useRef, useEffect } from 'react';function TextInputWithFocusButton() {const inputEl = useRef(null);useEffect(() => {inputEl.current?.focus();}, []);return <input ref={inputEl} type="text" />;}
In this example, the useRef hook is used to create a reference to the input element, and the useEffect hook is used to focus the input element when the component mounts. The optional chaining (?.) guards against the rare case where the element is not yet attached, which is a good habit under React 19's stricter dev-mode checks.
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 React, { useRef, useState, useEffect } from 'react';function Example() {const [count, setCount] = useState(0);const prevCountRef = useRef(undefined);useEffect(() => {prevCountRef.current = count;}, [count]);return (<div><h1>Now: {count}</h1><h2>Before: {prevCountRef.current}</h2><button onClick={() => setCount(count + 1)}>Increment</button></div>);}
Here prevCountRef holds the previous value of count across renders, but updating it does not itself cause a re-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 deprecated
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 deprecated and scheduled for removal 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.
<inputref={(node) => {node.focus();return () => {// runs when the element unmounts or the ref changes};}}/>