Focus management is essential for interactive React UIs, especially forms, modals, and dynamically rendered components.
Proper focus management improves keyboard navigation, accessibility, and user experience.
function MyComponent() {const inputRef = useRef<HTMLInputElement>(null)const [show, setShow] = useState(false)return (<div><buttononClick={() => {setShow(true)inputRef.current?.focus()}}>Show</button>{show && <input ref={inputRef} />}</div>)}
Why doesn’t this work?
Calling setShow(true) requests a state update. React has not yet committed the new input to the DOM when the next line executes, so inputRef.current is still null.
useEffectFocus the input after React commits the DOM update.
function MyComponent() {const inputRef = useRef<HTMLInputElement>(null)const [show, setShow] = useState(false)useEffect(() => {if (show) {inputRef.current?.focus()}}, [show])return (<div><button onClick={() => setShow(true)}>Show</button>{show && <input ref={inputRef} />}</div>)}
When show becomes true, React renders the input, attaches the ref, and then runs the effect.
This is usually sufficient for focus management because focusing an input does not generally require a synchronous DOM update within the click handler.
autoFocusFor a simple conditionally rendered input, you can also use:
{show && <input autoFocus />}
React will attempt to focus the input when it mounts. For more complex focus behavior, an explicit ref and effect provide greater control.
flushSync?flushSync forces React to flush updates synchronously so the DOM is updated before the next statement executes.
import { flushSync } from 'react-dom'function MyComponent() {const inputRef = useRef<HTMLInputElement>(null)const [show, setShow] = useState(false)return (<div><buttononClick={() => {flushSync(() => {setShow(true)})inputRef.current?.focus()}}>Show</button>{show && <input ref={inputRef} />}</div>)}
This works because the input has been committed to the DOM before focus() executes.
However, flushSync can hurt performance and may force pending work or effects to run synchronously. Use it only when an integration or imperative operation genuinely requires the DOM to be updated immediately.