Refs and DOM Access
Imperative escape hatches for DOM nodes and mutable values that should not trigger re-renders.
Search across all documentation pages
Imperative escape hatches for DOM nodes and mutable values that should not trigger re-renders.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
useRef holds a .current value that persists across renders without causing updates when it changes.
const renderCount = useRef(0);
renderCount.current += 1;Attach a ref to an input and call .focus() in a handler or effect after the node exists.
const inputRef = useRef<HTMLInputElement>(null);
function focusSearch() {
inputRef.current?.focus();
}
return <input ref={inputRef} />;Read layout from the DOM after commit. Prefer measuring in an event or layout effect when size drives UI.
const boxRef = useRef<HTMLDivElement>(null);
function measure() {
return boxRef.current?.getBoundingClientRect();
}Keep the last render's value in a ref so you can compare without forcing another state update.
const prev = useRef(value);
useEffect(() => {
prev.current = value;
}, [value]);
const previous = prev.current;Store timer ids in a ref so handlers can clear the latest timer without re-creating closures every tick.
const timerRef = useRef<number | null>(null);
function schedule() {
if (timerRef.current) window.clearTimeout(timerRef.current);
timerRef.current = window.setTimeout(run, 300);
}A function ref runs when the node attaches or detaches. Use it when you need setup tied to the actual DOM lifetime.
const setCanvas = useCallback((node: HTMLCanvasElement | null) => {
if (node) initChart(node);
else destroyChart();
}, []);
return <canvas ref={setCanvas} />;Legacy pattern to expose a child DOM node to a parent. Still valid; React 19 also allows ref as a normal prop on function components.
const Field = forwardRef<HTMLInputElement, FieldProps>(function Field(props, ref) {
return <input ref={ref} {...props} />;
});Customize what a parent receives when it holds a ref to your component - expose methods instead of the raw DOM node.
useImperativeHandle(ref, () => ({
focus: () => inputRef.current?.focus(),
clear: () => setValue(""),
}));Mirror the latest callback into a ref so a long-lived subscription always calls current logic without resubscribing.
const onMessageRef = useRef(onMessage);
onMessageRef.current = onMessage;
useEffect(() => socket.on("msg", (m) => onMessageRef.current(m)), [socket]);Writing/reading refs during render for control flow fights React's model. Prefer state for anything that should appear on screen.
// Good: read ref in handler
function onClick() {
console.log(ref.current?.value);
}Imperatively scroll a list item into view after selection or navigation.
const itemRef = useRef<HTMLLIElement>(null);
useEffect(() => {
if (selected) itemRef.current?.scrollIntoView({ block: "nearest" });
}, [selected]);Media elements expose imperative APIs. Drive play/pause from effects or handlers via a ref.
const videoRef = useRef<HTMLVideoElement>(null);
useEffect(() => {
if (playing) void videoRef.current?.play();
else videoRef.current?.pause();
}, [playing]);Call native reset() when you need the browser to clear uncontrolled fields, or reset controlled state instead.
const formRef = useRef<HTMLFormElement>(null);
function onClear() {
formRef.current?.reset();
}One ref can be read from multiple effects and handlers. It is a shared mutable cell for the component instance.
const nodeRef = useRef<HTMLDivElement>(null);
useEffect(() => observe(nodeRef.current), []);
useEffect(() => highlight(nodeRef.current, active), [active]);Always null-check .current. After unmount, React sets host refs to null; async work must tolerate that.
useEffect(() => {
let alive = true;
load().then((data) => {
if (alive) apply(ref.current, data);
});
return () => {
alive = false;
};
}, []);Stack versions: React 19 · TypeScript (strict) · DOM typings via
lib.dom
Reviewed by Chris St. John·Last updated Jul 18, 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥