React Refs and Callback Refs
React gives you two ways to grab a reference to a DOM element: object refs (via useRef) and callback refs. They both give you access to the underlying DOM...
24 Apr 2024

React gives you two ways to grab a reference to a DOM element: object refs (via useRef) and callback refs. They both give you access to the underlying DOM node, but they behave differently in ways that matter.
Object refs with useRef
useRef creates a mutable object with a .current property. You attach it to a JSX element, and React populates .current with the DOM node after render.
import React, { useRef, useEffect } from 'react'
const AutoFocusInput: React.FC = () => {
const inputRef = useRef<HTMLInputElement>(null)
useEffect(() => {
inputRef.current?.focus()
}, [])
return <input ref={inputRef} type="text" placeholder="I focus on mount" />
}
This is the approach most developers reach for. It's simple. The ref object persists across renders without causing re-renders. You access the DOM node whenever you need it through .current.
Callback refs
A callback ref is a function. React calls it with the DOM node when the component mounts, and calls it with null when it unmounts.
import React, { useCallback } from 'react'
const MeasuredBox: React.FC = () => {
const measuredRef = useCallback((node: HTMLDivElement | null) => {
if (node !== null) {
const { height } = node.getBoundingClientRect()
console.log(`Box height: ${height}px`)
}
}, [])
return <div ref={measuredRef}>Measure me</div>
}
The key difference: callback refs fire at the moment the node attaches or detaches. You don't need a useEffect to react to the node being available. This makes them ideal for measuring elements or integrating with third-party libraries that need the actual DOM node at initialization time.
When to use which
Use useRef when you need to store a reference and access it later -- focusing an input on a button click, scrolling to an element, or reading a value imperatively.
Use callback refs when you need to do something the instant the node appears or disappears -- measuring dimensions, initializing a third-party library, or tracking element visibility.
The trade-off
Object refs are simpler and cover 90% of use cases. Callback refs are more powerful but add complexity. If you wrap a callback ref in useCallback, you need to manage its dependency array. If you don't, React creates a new function on every render, and the callback fires on every render too (with null, then the node again).
Pick useRef by default. Reach for callback refs when timing matters.