{"id":4267,"date":"2026-08-14T04:10:50","date_gmt":"2026-08-14T04:10:50","guid":{"rendered":"https:\/\/tucumandevelopers.com\/index.php\/2026\/08\/14\/react-usefocus-hook-track-control-element-focus-state-2026\/"},"modified":"2026-08-14T04:10:50","modified_gmt":"2026-08-14T04:10:50","slug":"react-usefocus-hook-track-control-element-focus-state-2026","status":"publish","type":"post","link":"https:\/\/tucumandevelopers.com\/index.php\/2026\/08\/14\/react-usefocus-hook-track-control-element-focus-state-2026\/","title":{"rendered":"React useFocus Hook: Track &#038; Control Element Focus State (2026)"},"content":{"rendered":"<div>\n<div>\n<div data-article-id=\"4392740\" id=\"article-body\">\n<p>Focus is where interaction actually happens \u2014 the input receiving keystrokes, the button the keyboard user just tabbed to. Yet React gives you no state for it. <code>document.activeElement<\/code> knows the answer but never tells you when it changes, the <code>autoFocus<\/code> attribute fires once and can&#8217;t be re-triggered, and reading focus for <em>rendering<\/em> \u2014 show hints while editing, float a label, validate after leaving \u2014 means wiring <code>focus<\/code>\/<code>blur<\/code> listeners by hand on every field that needs it.<\/p>\n<p><a href=\"https:\/\/reactuse.com\/element\/usefocus\/\" target=\"_blank\" rel=\"noopener noreferrer\"><code>useFocus<\/code><\/a> from <a href=\"https:\/\/reactuse.com\/\" target=\"_blank\" rel=\"noopener noreferrer\"><code>@reactuses\/core<\/code><\/a> collapses all of that into one line: a live <code>isFocused<\/code> boolean your component just renders, plus a setter that focuses or blurs the element whenever your logic decides to.<\/p>\n<h2> <a name=\"quick-start\" href=\"#quick-start\"> <\/a> Quick Start <\/h2>\n<div>\n<pre><code>npm <span>install<\/span> @reactuses\/core <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<div>\n<pre><code><span>import<\/span> <span>{<\/span> <span>useRef<\/span> <span>}<\/span> <span>from<\/span> <span>\"<\/span><span>react<\/span><span>\"<\/span><span>;<\/span> <span>import<\/span> <span>{<\/span> <span>useFocus<\/span> <span>}<\/span> <span>from<\/span> <span>\"<\/span><span>@reactuses\/core<\/span><span>\"<\/span><span>;<\/span> <span>function<\/span> <span>SearchField<\/span><span>()<\/span> <span>{<\/span> <span>const<\/span> <span>ref<\/span> <span>=<\/span> <span>useRef<\/span><span>&lt;<\/span><span>HTMLInputElement<\/span><span>&gt;<\/span><span>(<\/span><span>null<\/span><span>);<\/span> <span>const<\/span> <span>[<\/span><span>isFocused<\/span><span>,<\/span> <span>setFocused<\/span><span>]<\/span> <span>=<\/span> <span>useFocus<\/span><span>(<\/span><span>ref<\/span><span>);<\/span> <span>return <\/span><span>(<\/span> <span>&lt;<\/span><span>div<\/span> <span>className<\/span><span>=<\/span><span>{<\/span><span>isFocused<\/span> <span>?<\/span> <span>\"<\/span><span>field field--active<\/span><span>\"<\/span> <span>:<\/span> <span>\"<\/span><span>field<\/span><span>\"<\/span><span>}<\/span><span>&gt;<\/span> <span>&lt;<\/span><span>input<\/span> <span>ref<\/span><span>=<\/span><span>{<\/span><span>ref<\/span><span>}<\/span> <span>placeholder<\/span><span>=<\/span><span>\"Search hooks\u2026\"<\/span> <span>\/&gt;<\/span> <span>{<\/span><span>isFocused<\/span> <span>&amp;&amp;<\/span> <span>&lt;<\/span><span>kbd<\/span> <span>className<\/span><span>=<\/span><span>\"hint\"<\/span><span>&gt;<\/span>esc to clear<span>&lt;\/<\/span><span>kbd<\/span><span>&gt;<\/span><span>}<\/span> <span>&lt;<\/span><span>button<\/span> <span>onClick<\/span><span>=<\/span><span>{<\/span><span>()<\/span> <span>=&gt;<\/span> <span>setFocused<\/span><span>(<\/span><span>true<\/span><span>)<\/span><span>}<\/span><span>&gt;<\/span>Jump to search<span>&lt;\/<\/span><span>button<\/span><span>&gt;<\/span> <span>&lt;\/<\/span><span>div<\/span><span>&gt;<\/span> <span>);<\/span> <span>}<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>That&#8217;s the whole integration. The hook subscribes to the element&#8217;s <code>focus<\/code> and <code>blur<\/code> events and mirrors them into state; <code>setFocused(true)<\/code> calls <code>element.focus()<\/code>, <code>setFocused(false)<\/code> calls <code>element.blur()<\/code>. One tuple, both directions \u2014 observe focus and command it.<\/p>\n<h2> <a name=\"why-not-autofocus-activeelement-or-plain-css\" href=\"#why-not-autofocus-activeelement-or-plain-css\"> <\/a> Why Not autoFocus, activeElement, or Plain CSS? <\/h2>\n<p>Each of the built-in options covers a sliver of the problem:<\/p>\n<ul>\n<li> <strong>CSS <code>:focus<\/code> \/ <code>:focus-within<\/code><\/strong> is the right tool when the response is <em>pure styling<\/em> \u2014 a border color, a glow. Use it; it costs zero JavaScript and zero re-renders. The hook earns its place the moment focus drives <strong>logic or JSX<\/strong>: rendering a hints panel, deciding <em>when<\/em> to validate, pausing a ticker while the user types.<\/li>\n<li> <strong><code>document.activeElement<\/code><\/strong> is a snapshot, not a subscription. Read it in render and it&#8217;s stale by the next tab-press; nothing re-renders your component when focus moves.<\/li>\n<li> <strong><code>autoFocus<\/code><\/strong> fires once, at mount, and that&#8217;s the entire API. It can&#8217;t focus on demand (&#8220;press <code>\/<\/code> to search&#8221;), can&#8217;t blur, and tells you nothing about the current state.<\/li>\n<li> <strong><code>ref.current.focus()<\/code> sprinkled in handlers<\/strong> works \u2014 until you also need to <em>know<\/em> whether the element is focused, and now you&#8217;re maintaining listeners anyway.<\/li>\n<\/ul>\n<h2> <a name=\"the-manual-way-and-where-it-bites\" href=\"#the-manual-way-and-where-it-bites\"> <\/a> The Manual Way \u2014 and Where It Bites <\/h2>\n<p>The hand-rolled version looks harmless: <\/p>\n<div>\n<pre><code><span>\/\/ \u26a0\ufe0f hand-rolled \u2014 works in the demo, leaks bugs in the app<\/span> <span>function<\/span> <span>SearchField<\/span><span>()<\/span> <span>{<\/span> <span>const<\/span> <span>ref<\/span> <span>=<\/span> <span>useRef<\/span><span>&lt;<\/span><span>HTMLInputElement<\/span><span>&gt;<\/span><span>(<\/span><span>null<\/span><span>);<\/span> <span>const<\/span> <span>[<\/span><span>isFocused<\/span><span>,<\/span> <span>setIsFocused<\/span><span>]<\/span> <span>=<\/span> <span>useState<\/span><span>(<\/span><span>false<\/span><span>);<\/span> <span>useEffect<\/span><span>(()<\/span> <span>=&gt;<\/span> <span>{<\/span> <span>const<\/span> <span>el<\/span> <span>=<\/span> <span>ref<\/span><span>.<\/span><span>current<\/span><span>;<\/span> <span>if <\/span><span>(<\/span><span>!<\/span><span>el<\/span><span>)<\/span> <span>return<\/span><span>;<\/span> <span>const<\/span> <span>onFocus<\/span> <span>=<\/span> <span>()<\/span> <span>=&gt;<\/span> <span>setIsFocused<\/span><span>(<\/span><span>true<\/span><span>);<\/span> <span>const<\/span> <span>onBlur<\/span> <span>=<\/span> <span>()<\/span> <span>=&gt;<\/span> <span>setIsFocused<\/span><span>(<\/span><span>false<\/span><span>);<\/span> <span>el<\/span><span>.<\/span><span>addEventListener<\/span><span>(<\/span><span>\"<\/span><span>focus<\/span><span>\"<\/span><span>,<\/span> <span>onFocus<\/span><span>);<\/span> <span>el<\/span><span>.<\/span><span>addEventListener<\/span><span>(<\/span><span>\"<\/span><span>blur<\/span><span>\"<\/span><span>,<\/span> <span>onBlur<\/span><span>);<\/span> <span>return <\/span><span>()<\/span> <span>=&gt;<\/span> <span>{<\/span> <span>el<\/span><span>.<\/span><span>removeEventListener<\/span><span>(<\/span><span>\"<\/span><span>focus<\/span><span>\"<\/span><span>,<\/span> <span>onFocus<\/span><span>);<\/span> <span>el<\/span><span>.<\/span><span>removeEventListener<\/span><span>(<\/span><span>\"<\/span><span>blur<\/span><span>\"<\/span><span>,<\/span> <span>onBlur<\/span><span>);<\/span> <span>};<\/span> <span>},<\/span> <span>[]);<\/span> <span>\/\/ \u2026<\/span> <span>}<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>Three problems hiding in fifteen lines:<\/p>\n<ol>\n<li> <strong>Late-mounting targets never get wired.<\/strong> The <code>if (!el) return<\/code> guard runs once. If the input renders conditionally \u2014 inside a modal, behind a tab, after a loading state \u2014 the effect has already returned and no listener ever attaches. An empty dependency array can&#8217;t express &#8220;re-run when the element appears.&#8221;<\/li>\n<li> <strong>It misses the initial state.<\/strong> If something focuses the element before your effect runs (an <code>autoFocus<\/code> attribute, a router&#8217;s focus restoration), your state says <code>false<\/code> while the element sits there focused. You need a <code>document.activeElement<\/code> check on mount <em>in addition to<\/em> the listeners.<\/li>\n<li> <strong>It&#8217;s per-field boilerplate.<\/strong> Multiply those fifteen lines by every input in a form and the form file is mostly plumbing.<\/li>\n<\/ol>\n<p><code>useFocus<\/code> absorbs all three: targets can be lazy getters (<code>() =&gt; document.querySelector(\".modal input\")<\/code>) that re-resolve as the DOM changes, mount-time state is reconciled for you, and each field is one line.<\/p>\n<h2> <a name=\"the-usefocus-api\" href=\"#the-usefocus-api\"> <\/a> The useFocus API <\/h2>\n<div>\n<pre><code><span>const<\/span> <span>[<\/span><span>isFocused<\/span><span>,<\/span> <span>setFocused<\/span><span>]<\/span> <span>=<\/span> <span>useFocus<\/span><span>(<\/span><span>target<\/span><span>,<\/span> <span>initialValue<\/span><span>?);<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p><strong><code>target<\/code><\/strong> is flexible \u2014 pass whichever you have: <\/p>\n<div>\n<pre><code><span>useFocus<\/span><span>(<\/span><span>ref<\/span><span>);<\/span> <span>\/\/ a ref object<\/span> <span>useFocus<\/span><span>(<\/span><span>document<\/span><span>.<\/span><span>getElementById<\/span><span>(<\/span><span>\"<\/span><span>search<\/span><span>\"<\/span><span>));<\/span> <span>\/\/ an element<\/span> <span>useFocus<\/span><span>(()<\/span> <span>=&gt;<\/span> <span>document<\/span><span>.<\/span><span>querySelector<\/span><span>(<\/span><span>\"<\/span><span>.otp input<\/span><span>\"<\/span><span>));<\/span> <span>\/\/ a lazy getter<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>SVG elements work too \u2014 the target type is <code>HTMLElement | SVGElement<\/code>, so a focusable <code>&lt;circle tabindex=\"0\"&gt;<\/code> in a chart is fair game.<\/p>\n<p><strong><code>initialValue<\/code><\/strong> (default <code>false<\/code>) is declarative autofocus: pass <code>true<\/code> and the hook focuses the element on mount. Unlike the <code>autoFocus<\/code> attribute, it goes through the same code path as <code>setFocused<\/code>, works with getter targets, and leaves you holding the live state afterwards.<\/p>\n<p><strong><code>setFocused<\/code><\/strong> is imperative control with cleanup included: <code>true<\/code> \u2192 <code>element.focus()<\/code>, <code>false<\/code> \u2192 <code>element.blur()<\/code>. If the target doesn&#8217;t exist yet, the call is a safe no-op instead of a crash.<\/p>\n<h2> <a name=\"real-patterns\" href=\"#real-patterns\"> <\/a> Real Patterns <\/h2>\n<h3> <a name=\"floating-labels-the-label-knows-when-to-float\" href=\"#floating-labels-the-label-knows-when-to-float\"> <\/a> Floating labels \u2014 the label knows when to float <\/h3>\n<p>The material-style input: label sits inside the field, floats up when the field is active <em>or<\/em> has content. <\/p>\n<div>\n<pre><code><span>function<\/span> <span>FloatingLabelInput<\/span><span>({<\/span> <span>label<\/span> <span>}:<\/span> <span>{<\/span> <span>label<\/span><span>:<\/span> <span>string<\/span> <span>})<\/span> <span>{<\/span> <span>const<\/span> <span>ref<\/span> <span>=<\/span> <span>useRef<\/span><span>&lt;<\/span><span>HTMLInputElement<\/span><span>&gt;<\/span><span>(<\/span><span>null<\/span><span>);<\/span> <span>const<\/span> <span>[<\/span><span>isFocused<\/span><span>]<\/span> <span>=<\/span> <span>useFocus<\/span><span>(<\/span><span>ref<\/span><span>);<\/span> <span>const<\/span> <span>[<\/span><span>value<\/span><span>,<\/span> <span>setValue<\/span><span>]<\/span> <span>=<\/span> <span>useState<\/span><span>(<\/span><span>\"\"<\/span><span>);<\/span> <span>const<\/span> <span>floated<\/span> <span>=<\/span> <span>isFocused<\/span> <span>||<\/span> <span>value<\/span><span>.<\/span><span>length<\/span> <span>&gt;<\/span> <span>0<\/span><span>;<\/span> <span>return <\/span><span>(<\/span> <span>&lt;<\/span><span>label<\/span> <span>className<\/span><span>=<\/span><span>\"float-field\"<\/span><span>&gt;<\/span> <span>&lt;<\/span><span>span<\/span> <span>className<\/span><span>=<\/span><span>{<\/span><span>floated<\/span> <span>?<\/span> <span>\"<\/span><span>float-label up<\/span><span>\"<\/span> <span>:<\/span> <span>\"<\/span><span>float-label<\/span><span>\"<\/span><span>}<\/span><span>&gt;<\/span> <span>{<\/span><span>label<\/span><span>}<\/span> <span>&lt;\/<\/span><span>span<\/span><span>&gt;<\/span> <span>&lt;<\/span><span>input<\/span> <span>ref<\/span><span>=<\/span><span>{<\/span><span>ref<\/span><span>}<\/span> <span>value<\/span><span>=<\/span><span>{<\/span><span>value<\/span><span>}<\/span> <span>onChange<\/span><span>=<\/span><span>{<\/span><span>e<\/span> <span>=&gt;<\/span> <span>setValue<\/span><span>(<\/span><span>e<\/span><span>.<\/span><span>target<\/span><span>.<\/span><span>value<\/span><span>)<\/span><span>}<\/span> <span>\/&gt;<\/span> <span>&lt;\/<\/span><span>label<\/span><span>&gt;<\/span> <span>);<\/span> <span>}<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>CSS alone gets close with <code>:focus-within<\/code> + <code>:placeholder-shown<\/code>, but the moment the float condition involves app state \u2014 a controlled value, a validation flag \u2014 you need focus <em>as state<\/em>, and this is it.<\/p>\n<h3> <a name=\"validate-on-blur-not-on-keystroke\" href=\"#validate-on-blur-not-on-keystroke\"> <\/a> Validate on blur, not on keystroke <\/h3>\n<p>Yelling &#8220;invalid email&#8221; at someone who has typed three characters is the classic form-UX failure. The fix is <em>touched<\/em> semantics \u2014 validate only after the user leaves the field: <\/p>\n<div>\n<pre><code><span>function<\/span> <span>EmailField<\/span><span>()<\/span> <span>{<\/span> <span>const<\/span> <span>ref<\/span> <span>=<\/span> <span>useRef<\/span><span>&lt;<\/span><span>HTMLInputElement<\/span><span>&gt;<\/span><span>(<\/span><span>null<\/span><span>);<\/span> <span>const<\/span> <span>[<\/span><span>isFocused<\/span><span>]<\/span> <span>=<\/span> <span>useFocus<\/span><span>(<\/span><span>ref<\/span><span>);<\/span> <span>const<\/span> <span>[<\/span><span>value<\/span><span>,<\/span> <span>setValue<\/span><span>]<\/span> <span>=<\/span> <span>useState<\/span><span>(<\/span><span>\"\"<\/span><span>);<\/span> <span>const<\/span> <span>[<\/span><span>touched<\/span><span>,<\/span> <span>setTouched<\/span><span>]<\/span> <span>=<\/span> <span>useState<\/span><span>(<\/span><span>false<\/span><span>);<\/span> <span>useEffect<\/span><span>(()<\/span> <span>=&gt;<\/span> <span>{<\/span> <span>if <\/span><span>(<\/span><span>isFocused<\/span><span>)<\/span> <span>return<\/span><span>;<\/span> <span>\/\/ still editing \u2014 stay quiet<\/span> <span>if <\/span><span>(<\/span><span>value<\/span><span>)<\/span> <span>setTouched<\/span><span>(<\/span><span>true<\/span><span>);<\/span> <span>\/\/ left the field with content \u2192 judge it<\/span> <span>},<\/span> <span>[<\/span><span>isFocused<\/span><span>,<\/span> <span>value<\/span><span>]);<\/span> <span>const<\/span> <span>error<\/span> <span>=<\/span> <span>touched<\/span> <span>&amp;&amp;<\/span> <span>!<\/span><span>isFocused<\/span> <span>&amp;&amp;<\/span> <span>!<\/span><span>value<\/span><span>.<\/span><span>includes<\/span><span>(<\/span><span>\"<\/span><span>@<\/span><span>\"<\/span><span>);<\/span> <span>return <\/span><span>(<\/span> <span>&lt;<\/span><span>div<\/span><span>&gt;<\/span> <span>&lt;<\/span><span>input<\/span> <span>ref<\/span><span>=<\/span><span>{<\/span><span>ref<\/span><span>}<\/span> <span>value<\/span><span>=<\/span><span>{<\/span><span>value<\/span><span>}<\/span> <span>onChange<\/span><span>=<\/span><span>{<\/span><span>e<\/span> <span>=&gt;<\/span> <span>setValue<\/span><span>(<\/span><span>e<\/span><span>.<\/span><span>target<\/span><span>.<\/span><span>value<\/span><span>)<\/span><span>}<\/span> <span>\/&gt;<\/span> <span>{<\/span><span>error<\/span> <span>&amp;&amp;<\/span> <span>&lt;<\/span><span>p<\/span> <span>className<\/span><span>=<\/span><span>\"error\"<\/span><span>&gt;<\/span>That doesn't look like an email.<span>&lt;\/<\/span><span>p<\/span><span>&gt;<\/span><span>}<\/span> <span>&lt;\/<\/span><span>div<\/span><span>&gt;<\/span> <span>);<\/span> <span>}<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>The <code>isFocused<\/code> transition <em>is<\/em> the touched signal \u2014 no <code>onBlur<\/code> prop threading, and the error clears itself the moment the user comes back to fix it.<\/p>\n<h3> <a name=\"press-raw-endraw-to-search\" href=\"#press-raw-endraw-to-search\"> <\/a> Press <code>\/<\/code> to search <\/h3>\n<p>Every documentation site does this, and <code>setFocused<\/code> plus <a href=\"https:\/\/reactuse.com\/effect\/useeventlistener\/\" target=\"_blank\" rel=\"noopener noreferrer\"><code>useEventListener<\/code><\/a> is the entire implementation: <\/p>\n<div>\n<pre><code><span>function<\/span> <span>DocSearch<\/span><span>()<\/span> <span>{<\/span> <span>const<\/span> <span>ref<\/span> <span>=<\/span> <span>useRef<\/span><span>&lt;<\/span><span>HTMLInputElement<\/span><span>&gt;<\/span><span>(<\/span><span>null<\/span><span>);<\/span> <span>const<\/span> <span>[<\/span><span>isFocused<\/span><span>,<\/span> <span>setFocused<\/span><span>]<\/span> <span>=<\/span> <span>useFocus<\/span><span>(<\/span><span>ref<\/span><span>);<\/span> <span>useEventListener<\/span><span>(<\/span><span>\"<\/span><span>keydown<\/span><span>\"<\/span><span>,<\/span> <span>(<\/span><span>e<\/span><span>)<\/span> <span>=&gt;<\/span> <span>{<\/span> <span>if <\/span><span>(<\/span><span>e<\/span><span>.<\/span><span>key<\/span> <span>===<\/span> <span>\"<\/span><span>\/<\/span><span>\"<\/span> <span>&amp;&amp;<\/span> <span>!<\/span><span>isFocused<\/span><span>)<\/span> <span>{<\/span> <span>e<\/span><span>.<\/span><span>preventDefault<\/span><span>();<\/span> <span>\/\/ don't type the slash<\/span> <span>setFocused<\/span><span>(<\/span><span>true<\/span><span>);<\/span> <span>}<\/span> <span>if <\/span><span>(<\/span><span>e<\/span><span>.<\/span><span>key<\/span> <span>===<\/span> <span>\"<\/span><span>Escape<\/span><span>\"<\/span><span>)<\/span> <span>setFocused<\/span><span>(<\/span><span>false<\/span><span>);<\/span> <span>});<\/span> <span>return<\/span> <span>&lt;<\/span><span>input<\/span> <span>ref<\/span><span>=<\/span><span>{<\/span><span>ref<\/span><span>}<\/span> <span>placeholder<\/span><span>=<\/span><span>\"Press \/ to search\"<\/span> <span>\/&gt;;<\/span> <span>}<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>Note how both halves of the tuple earn their keep: <code>isFocused<\/code> guards against hijacking a <code>\/<\/code> the user is legitimately typing <em>into the field<\/em>, and <code>setFocused<\/code> does the jump.<\/p>\n<h3> <a name=\"autofocus-that-survives-conditional-rendering\" href=\"#autofocus-that-survives-conditional-rendering\"> <\/a> Autofocus that survives conditional rendering <\/h3>\n<p>Focusing the first field of a modal form, where the input doesn&#8217;t exist until <a href=\"https:\/\/reactuse.com\/state\/usedisclosure\/\" target=\"_blank\" rel=\"noopener noreferrer\"><code>useDisclosure<\/code><\/a> says so: <\/p>\n<div>\n<pre><code><span>function<\/span> <span>RenameDialog<\/span><span>({<\/span> <span>open<\/span> <span>}:<\/span> <span>{<\/span> <span>open<\/span><span>:<\/span> <span>boolean<\/span> <span>})<\/span> <span>{<\/span> <span>const<\/span> <span>ref<\/span> <span>=<\/span> <span>useRef<\/span><span>&lt;<\/span><span>HTMLInputElement<\/span><span>&gt;<\/span><span>(<\/span><span>null<\/span><span>);<\/span> <span>useFocus<\/span><span>(<\/span><span>ref<\/span><span>,<\/span> <span>true<\/span><span>);<\/span> <span>\/\/ focuses on mount \u2014 which is when the dialog opens<\/span> <span>if <\/span><span>(<\/span><span>!<\/span><span>open<\/span><span>)<\/span> <span>return<\/span> <span>null<\/span><span>;<\/span> <span>return <\/span><span>(<\/span> <span>&lt;<\/span><span>dialog<\/span> <span>open<\/span><span>&gt;<\/span> <span>&lt;<\/span><span>input<\/span> <span>ref<\/span><span>=<\/span><span>{<\/span><span>ref<\/span><span>}<\/span> <span>defaultValue<\/span><span>=<\/span><span>\"untitled.md\"<\/span> <span>\/&gt;<\/span> <span>&lt;\/<\/span><span>dialog<\/span><span>&gt;<\/span> <span>);<\/span> <span>}<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>Because the component (and the hook) mounts when the dialog opens, <code>initialValue: true<\/code> fires at exactly the right moment \u2014 no <code>setTimeout(\u2026, 0)<\/code> incantations.<\/p>\n<h2> <a name=\"usefocus-vs-its-siblings\" href=\"#usefocus-vs-its-siblings\"> <\/a> useFocus vs Its Siblings <\/h2>\n<p><code>@reactuses\/core<\/code> ships three focus hooks at three zoom levels \u2014 pick by the question you&#8217;re asking:<\/p>\n<div>\n<table>\n<thead>\n<tr>\n<th>Hook<\/th>\n<th>Answers<\/th>\n<th>Reach for it when<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><a href=\"https:\/\/reactuse.com\/element\/usefocus\/\" target=\"_blank\" rel=\"noopener noreferrer\"><code>useFocus<\/code><\/a><\/td>\n<td>&#8220;is <strong>this element<\/strong> focused?&#8221; + control<\/td>\n<td>per-field UI: labels, hints, validation timing, shortcuts<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/reactuse.com\/element\/useactiveelement\/\" target=\"_blank\" rel=\"noopener noreferrer\"><code>useActiveElement<\/code><\/a><\/td>\n<td>&#8220;<strong>which element<\/strong> has focus, document-wide?&#8221;<\/td>\n<td>form-level logic, focus debugging, roving-focus widgets<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/reactuse.com\/element\/usewindowfocus\/\" target=\"_blank\" rel=\"noopener noreferrer\"><code>useWindowFocus<\/code><\/a><\/td>\n<td>&#8220;is the <strong>tab\/window<\/strong> focused at all?&#8221;<\/td>\n<td>pausing polling or animations when the user switches away<\/td>\n<\/tr>\n<tr>\n<td>CSS <code>:focus<\/code> \/ <code>:focus-visible<\/code> <\/td>\n<td>styling only<\/td>\n<td>any pure-CSS response \u2014 always try this first<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>The rule of thumb: one element \u2192 <code>useFocus<\/code>; whole document \u2192 <a href=\"https:\/\/reactuse.com\/element\/useactiveelement\/\" target=\"_blank\" rel=\"noopener noreferrer\"><code>useActiveElement<\/code><\/a>; the browser window itself \u2192 <a href=\"https:\/\/reactuse.com\/element\/usewindowfocus\/\" target=\"_blank\" rel=\"noopener noreferrer\"><code>useWindowFocus<\/code><\/a>.<\/p>\n<h2> <a name=\"production-notes\" href=\"#production-notes\"> <\/a> Production Notes <\/h2>\n<ul>\n<li> <strong>SSR is handled.<\/strong> There&#8217;s no DOM on the server; the hook renders the <code>initialValue<\/code> you gave it and wires listeners after hydration \u2014 no <code>typeof window<\/code> guards in your code.<\/li>\n<li> <strong><code>element.focus()<\/code> scrolls.<\/strong> Browsers scroll a newly focused element into view. Autofocusing something below the fold on page load yanks the viewport \u2014 reserve <code>initialValue: true<\/code> for elements that are already where the user is looking (modals, inline editors).<\/li>\n<li> <strong>Don&#8217;t steal focus.<\/strong> Moving focus is an accessibility action, not a visual one: screen readers announce the newly focused element, and keyboard users lose their place. Focus in response to <em>user intent<\/em> (a shortcut, opening a dialog), never on a timer or a data refresh.<\/li>\n<li> <strong>Blur sends focus to <code>&lt;body&gt;<\/code>.<\/strong> <code>setFocused(false)<\/code> doesn&#8217;t restore focus to where it was before \u2014 after closing a dialog, hand focus back to the trigger button explicitly.<\/li>\n<li> <strong>Style with <code>:focus-visible<\/code>, decide with <code>isFocused<\/code>.<\/strong> Keyboard-only focus rings are a solved CSS problem; keep the ring in CSS and spend the hook&#8217;s state on logic. Related concerns compose the same way \u2014 clicks outside the field are <a href=\"https:\/\/reactuse.com\/element\/useclickoutside\/\" target=\"_blank\" rel=\"noopener noreferrer\"><code>useClickOutside<\/code><\/a>, not a blur hack.<\/li>\n<\/ul>\n<h2> <a name=\"takeaways\" href=\"#takeaways\"> <\/a> Takeaways <\/h2>\n<ul>\n<li>React has no focus state, and the primitives don&#8217;t compose into one: <code>document.activeElement<\/code> isn&#8217;t reactive, <code>autoFocus<\/code> is fire-once, and hand-rolled listeners miss late-mounting elements and pre-focused mounts. <a href=\"https:\/\/reactuse.com\/element\/usefocus\/\" target=\"_blank\" rel=\"noopener noreferrer\"><code>useFocus<\/code><\/a> is the missing <code>[isFocused, setFocused]<\/code> tuple.<\/li>\n<li>The setter is bidirectional control \u2014 <code>true<\/code> focuses, <code>false<\/code> blurs, safely no-oping if the element isn&#8217;t there yet; <code>initialValue: true<\/code> is declarative autofocus that lands exactly at mount.<\/li>\n<li>The killer patterns are timing patterns: float labels while editing, validate only after leaving, jump to search on <code>\/<\/code>, focus the modal&#8217;s first field the instant it exists.<\/li>\n<li>Pure styling belongs to CSS <code>:focus<\/code> and <code>:focus-visible<\/code> \u2014 spend the hook on logic and JSX. And pick your zoom level: element \u2192 <code>useFocus<\/code>, document \u2192 <a href=\"https:\/\/reactuse.com\/element\/useactiveelement\/\" target=\"_blank\" rel=\"noopener noreferrer\"><code>useActiveElement<\/code><\/a>, window \u2192 <a href=\"https:\/\/reactuse.com\/element\/usewindowfocus\/\" target=\"_blank\" rel=\"noopener noreferrer\"><code>useWindowFocus<\/code><\/a>.<\/li>\n<li>Focus is an accessibility surface: move it on user intent, never steal it, and return it where it came from when you&#8217;re done.<\/li>\n<\/ul>\n<p><code>useFocus<\/code> and 110+ other SSR-safe, TypeScript-first hooks live in <a href=\"https:\/\/reactuse.com\/\" target=\"_blank\" rel=\"noopener noreferrer\"><code>@reactuses\/core<\/code><\/a> \u2014 one install, tree-shakeable, no dependencies to babysit. <\/p>\n<div>\n<pre><code>npm <span>install<\/span> @reactuses\/core <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<\/p><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Fuente: <a href=\"https:\/\/dev.to\/childrentime\/react-usefocus-hook-track-control-element-focus-state-2026-5ffh\">Art\u00edculo original<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Focus is where interaction actually happens \u2014 the input receiving keystrokes, the button the keyboard user just tabbed to. Yet React gives you no state for it. document.activeElement knows the answer but never tells you when it changes, the autoFocus attribute fires once and can&#8217;t be re-triggered, and reading focus for rendering \u2014 show hints [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":4266,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"webixso_pending_account_ids":""},"categories":[41],"tags":[],"class_list":["post-4267","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devto"],"jetpack_publicize_connections":[],"_links":{"self":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts\/4267","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/comments?post=4267"}],"version-history":[{"count":0,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts\/4267\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/media\/4266"}],"wp:attachment":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/media?parent=4267"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/categories?post=4267"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/tags?post=4267"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}