Manipulate the DOM and keep instance values with useRef, without breaking render purity.
Open this lesson in KodokonuseRef returns a mutable box - an object with a single current property - whose identity survives every render. Two fundamental differences from state: modifying ref.current triggers no re-render, and the change is visible immediately, without waiting for the next render. Two families of use follow: referencing a DOM node, and storing an instance value that must survive renders without driving the display.
import { useEffect, useRef } from "react";
function SearchField() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
}, []);
return <input ref={inputRef} type="search" />;
}On the instance-value side, anything that must persist without causing a render belongs in a ref: the id returned by setInterval, the instance of an external library (a map, an editor, a video player), or a value from a previous render. The golden rule remains the same: rendering must be a pure function of props and state. Refs are read and written in effects and event handlers, not during render.
import { useEffect, useRef } from "react";
function usePrevious(value) {
const ref = useRef(undefined);
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}To expose a DOM node from your own component, pass the ref along: since React 19, ref is an ordinary prop of function components; before that, going through forwardRef was mandatory. One last reflex to cultivate: prefer the declarative approach when state is enough to describe the result (a CSS class, a disabled attribute). Reserve refs for the territory React does not cover: focus, text selection, measuring an element, scrolling, media playback.
import { useRef } from "react";
function TextField({ ref, ...props }) {
return <input ref={ref} {...props} />;
}
function Form() {
const fieldRef = useRef(null);
return (
<>
<TextField ref={fieldRef} />
<button onClick={() => fieldRef.current.focus()}>
Give focus
</button>
</>
);
}