useRef & the DOM
A ref is a box that survives renders
Section titled “A ref is a box that survives renders”useRef gives you a mutable container — an object { current: value } — that React keeps across renders but does not watch. Changing ref.current does not trigger a re-render.
const ref = useRef(0);ref.current += 1; // mutate freely — NO re-render happensThat’s the whole difference from state:
useState | useRef | |
|---|---|---|
| Survives re-renders | ✅ | ✅ |
| Changing it re-renders | ✅ yes | ❌ no |
| Read during render | ✅ (it’s the snapshot) | ⚠️ avoid (mutable, not reactive) |
| Use for | data shown in the UI | values you need to remember but not render |
Use a ref for things the UI doesn’t directly display: a timer id, the previous value of something, a mutable counter, a scroll position, or a reference to a DOM node.
// Classic use: hold a timer id so you can clear it later.const intervalRef = useRef(null);function start() { intervalRef.current = setInterval(tick, 1000);}function stop() { clearInterval(intervalRef.current);}Refs for DOM access
Section titled “Refs for DOM access”The most common use is grabbing a DOM node — to focus an input, measure an element, or call an imperative browser API. Attach the ref to a host element via the ref attribute; after commit, ref.current points at the real DOM node.
function SearchBox() { const inputRef = useRef(null); return ( <> <input ref={inputRef} /> <button onClick={() => inputRef.current.focus()}>Focus</button> </> );}This is an escape hatch — you’re stepping outside the declarative model to talk to the DOM directly. Use it for focus, scrolling, measurements, and integrating non-React widgets; don’t use it to do things state can do.
React 19: ref is just a prop
Section titled “React 19: ref is just a prop”Previously, passing a ref through your own component to a DOM node inside it required wrapping the component in forwardRef. In React 19, ref is a regular prop — you can accept it and pass it down like any other prop, and forwardRef is no longer needed for new code.
// ✅ React 19: accept ref as a normal prop — no forwardRef.function MyInput({ placeholder, ref }) { return <input placeholder={placeholder} ref={ref} />;}
// Parent:const ref = useRef(null);<MyInput ref={ref} placeholder="Search" />;The old form still works — forwardRef is not removed — but it’s now legacy. New components should take ref as a prop.
For the rarer case where you want to expose a custom imperative API (not the raw DOM node) to a parent, useImperativeHandle lets you define exactly what the ref exposes:
function FancyInput({ ref }) { const inputRef = useRef(null); useImperativeHandle(ref, () => ({ focus: () => inputRef.current.focus(), clear: () => { inputRef.current.value = ''; }, })); return <input ref={inputRef} />;}