Working with DOM Refs
Working with DOM Refs
A teacher opens a student search panel and presses Focus search. The application needs to move keyboard focus to the actual browser input, which a string state value cannot do.
A DOM ref gives React a container or callback for an element it commits to the browser document. Once the node exists, an event handler can use its browser methods, such as focus, for a targeted imperative action.
Understand
Attach a ref to a native input element.
Build
Use a browser node only after it has been committed.
Check
Keep element content controlled by React rather than rewriting it manually.
Describe the node, then use its handle
Render description
JSX describes an input with a ref.
Commit
React creates or updates the real browser input.
Event
A button reads the node and calls focus.
Rendering and committing are different
Rendering is React calculating the element description by calling components. Committing is React applying the corresponding changes to browser elements. Before a newly described input has been committed, inputRef.current may still be null. A ref is therefore not a way to inspect a new browser element in the middle of rendering.
Focusing is an imperative browser operation: it tells an existing element to do something. That complements React’s declarative description of what the input should display. Use a ref for focused operations such as focus or scrolling, and keep the field’s value in the ordinary state and props data flow.
Focus a labeled student search
The native input receives ref={inputRef}. After the page commits, its DOM node becomes available through current. Focus search reads that node in a click handler. A status message is updated through state so the visible confirmation does not depend on a ref mutation causing a render.
React · JavaScript with JSX
import { useRef, useState } from 'react';
export default function App() {
const inputRef = useRef(null);
const [message, setMessage] = useState('Search ready');
function focusSearch() {
inputRef.current?.focus();
setMessage('Search focused');
}
return <main><h1>Student search focus</h1>
<label>Student search <input ref={inputRef} /></label>
<button onClick={focusSearch}>Focus search</button>
<p role="status">{message}</p>
</main>;
}Expected screen and behavior
Focus search moves keyboard focus into the labeled field and changes the message to Search focused.Wait for an actual input node
React · Focus a labeled student search
import { useRef, useState } from 'react';
export default function App() {
const inputRef = useRef(null);
const [message, setMessage] = useState('Search ready');
function focusSearch() {
inputRef.current?.focus();
setMessage('Search focused');
}
return <main><h1>Student search focus</h1>
<label>Student search <input ref={inputRef} /></label>
<button onClick={focusSearch}>Focus search</button>
<p role="status">{message}</p>
</main>;
}The initial render returns an input description containing the ref container. During the commit, React attaches the browser input node to current. The teacher can then click the button; the handler reads that committed node and calls its native focus method.
Optional chaining avoids calling focus on null if the node is unavailable. When React removes the input, the object ref no longer points to the removed mounted node. Code handling a conditionally displayed field must account for that lifecycle. Immediately showing a field with setShow and then focusing it in the same handler may be too early because the new node has not been committed yet.
Keep focus useful to the teacher
Moving focus should support the teacher’s next action, not surprise them during unrelated edits. A deliberate Focus search button is clear. Opening a dialog or returning to an error field can also justify focus management, but the surrounding interaction should remain predictable for keyboard users.
Refs on custom components depend on what that component exposes. A native input has a browser node directly. A custom component may need a supported ref contract; React 19 allows function components to receive ref as a prop, while earlier patterns often use forwardRef. This lesson attaches refs only to native elements, so no version-specific forwarding setup is required.
Try the school task
React · starting example
import { useRef, useState } from 'react';
export default function App() {
const inputRef = useRef(null);
const [message, setMessage] = useState('Search ready');
function focusSearch() {
inputRef.current?.focus();
setMessage('Search focused');
}
return <main><h1>Student search focus</h1>
<label>Student search <input ref={inputRef} /></label>
<button onClick={focusSearch}>Focus search</button>
<p role="status">{message}</p>
</main>;
}Make Clear and focus empty a controlled Student search field and move focus back into it. Show the current query on the page. After entering Ravi and clicking the button, the query display should read (empty), and typing can continue in the same field.
Worked answer
The answer combines state for the value and a ref for focus. setQuery clears the rendered text, while focus targets the already mounted input. It does not remove or replace the input, so there is no need to wait for a newly created element. The two tools have different responsibilities.
React · JavaScript with JSX
import { useRef, useState } from 'react';
export default function App() {
const inputRef = useRef(null);
const [query, setQuery] = useState('');
function clear() { setQuery(''); inputRef.current?.focus(); }
return <main><h1>Clear student search</h1>
<label>Student search <input ref={inputRef} value={query} onChange={event => setQuery(event.target.value)} /></label>
<p>Query: {query || '(empty)'}</p>
<button onClick={clear}>Clear and focus</button>
</main>;
}Expected screen and behavior
Entering Ravi shows Query: Ravi. Clear and focus empties the value and focuses the existing input.Mistakes and boundaries
Do not manually remove children or overwrite text inside a subtree React manages. React’s next commit expects the structure it described. Using focus on a node is a targeted operation; rewriting the rendered attendance list with innerHTML creates conflicting ownership and can also expose unsafe markup.
A message saying Search focused is only one visible observation. Browser verification should also check the active element for a focus task. Extra StrictMode development checks can expose missing ref cleanup in callback patterns; do not rely on a fixed number of ref attachments as application behavior.
Check your reasoning
React · Focus a labeled student search
import { useRef, useState } from 'react';
export default function App() {
const inputRef = useRef(null);
const [message, setMessage] = useState('Search ready');
function focusSearch() {
inputRef.current?.focus();
setMessage('Search focused');
}
return <main><h1>Student search focus</h1>
<label>Student search <input ref={inputRef} /></label>
<button onClick={focusSearch}>Focus search</button>
<p role="status">{message}</p>
</main>;
}Choose the answer that follows the data and the interaction, then read the explanation for each alternative.
Carry the idea forward
Refs can target committed browser nodes for focus and other limited operations. Keep visible values in React’s data flow. Next, useEffect explains synchronization that follows committed renders rather than a particular button action.
Read the primary reference: React: manipulating the DOM with refs.
Reaching the recap and completing the quizzes are separate milestones.