Master React Refs: From DOM Access to Imperative APIs
Learn how to use React refs—including useRef, forwardRef, and useImperativeHandle—to access DOM elements, create custom imperative APIs, and build interactive forms with focus and shake effects, while understanding best practices for when to employ refs versus state or props.
This article is translated from “Refs in React: from access to DOM to imperative API”.
React abstracts DOM manipulation, but sometimes you need to access real DOM elements.
Using useRef to access DOM
Example of focusing an empty field, etc.
<code>const element = document.getElementById("bla");</code> <code>element.focus();</code> <code>element.scrollIntoView();</code>Typical scenarios for using native DOM API in React:
Manually focusing an element after render, such as an input field.
Detecting clicks outside a popup component.
Scrolling to an element that appears on screen.
Measuring component size and bounds to position tooltips.
React provides refs as a more powerful way to access elements without IDs.
A ref is a mutable object created with
useRef. It holds a
currentproperty that can store any value, including a DOM node.
<code>const Component = () => {
const ref = useRef(null);
return ...;
}</code>You can assign a ref to any DOM element or component:
<code>return <input ref={ref} />;</code>When the component mounts,
ref.currentpoints to the input element, similar to
getElementById.
Passing ref as a prop
Extract input fields into a separate component and pass a ref from the parent:
<code>const InputField = ({ onChange, label }) => (
<>
{label}<br/>
<input type="text" onChange={e => onChange(e.target.value)} />
</>
);</code>The parent creates the ref and forwards it:
<code>const Form = () => {
const inputRef = useRef(null);
return (
<>
<InputField inputRef={inputRef} />
<button onClick={onSubmitClick}>Submit</button>
</>
);
};</code>Using forwardRef
Function components don’t have instances, so you must wrap them with
forwardRefto accept a ref:
<code>const InputField = forwardRef((props, ref) => (
<input ref={ref} />
));</code>The parent can then pass the ref directly:
<code><InputField ref={inputRef} /></code>Creating an imperative API with useImperativeHandle
To expose methods like
focusand
shakeon a component, use
useImperativeHandle:
<code>useImperativeHandle(apiRef, () => ({
focus: () => inputRef.current.focus(),
shake: () => setShouldShake(true),
}), []);</code>The parent can call
apiRef.current.focus()or
apiRef.current.shake()without knowing the internal implementation.
Alternative without useImperativeHandle
You can assign the API object directly in an effect:
<code>useEffect(() => {
apiRef.current = {
focus: () => inputRef.current.focus(),
shake: () => setShouldShake(true),
};
}, [apiRef]);</code>Both approaches give a clean way to implement command‑style actions while keeping refs as an emergency escape hatch, not a replacement for state or props.
KooFE Frontend Team
Follow the latest frontend updates
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.