Chapter 35: Manipulating the DOM with Refs
Core Idea
Passing a ref to a built-in element's ref attribute gives you direct, imperative access to the underlying DOM node (for things React's declarative model doesn't cover, like focusing, scrolling, or measuring) — but this should stay a targeted escape hatch, not the default way to change what's on screen.
Key Concepts
- Getting a ref to a node:
const inputRef = useRef(null), then <input ref={inputRef} /> — after mount, inputRef.current is the real DOM element, letting you call imperative browser APIs (.focus(), .scrollIntoView(), .measure()-style reads) directly.
- Accessing another component's DOM node: by default, a custom component doesn't expose its inner DOM node to a ref the way a built-in element does — a component needs to explicitly forward that access (historically via
forwardRef, and directly via a ref prop in modern React) if a parent needs to reach into its rendered DOM.
- When React attaches refs: refs are attached after the commit phase (Ch 22) updates the DOM — reading
ref.current inside the render body itself is too early and will see a stale/null value; refs are safe to read in event handlers and Effects, which run after commit.
- Best practices: use refs only for interactions React doesn't model declaratively (focus management, media playback control, text selection, scroll position, size/position measurement, non-React widget integration) — never use a ref to read-then-manually-mutate a DOM node's content/attributes that React itself is also managing, since the two will fight and produce inconsistent results.
Code Examples
function SearchInput() {
const inputRef = useRef(null);
return (
<>
<input ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>Focus</button>
</>
);
}
- What it demonstrates: the canonical ref use case — imperatively calling
.focus() on a real DOM node in response to a click, something no declarative prop expresses.
Key Takeaways
- Reserve DOM refs for genuinely imperative browser APIs (focus, scroll, media, measurement) — if a change can be expressed as JSX/props instead, prefer that.
- Never manually mutate a DOM node's attributes/content that React also renders via that same node — the two will conflict on the next render.
- A ref only reliably points at the mounted node after commit — access it from event handlers or Effects, never mid-render.
Connects To
- Ch 34 (Referencing Values with Refs): the general-purpose ref mechanic this chapter specializes to DOM nodes.
- Ch 22 (Render and Commit): the commit step that must complete before a ref's DOM node is available.
- Ch 91 (use) and Ch 85 (forwardRef): the modern and legacy ways a custom component exposes its DOM node to a parent's ref.