useFocusWithin
Events & gesturesTrack whether keyboard focus is currently anywhere within a subtree — the reactive equivalent of CSS <code>:focus-within</code>
Install
$ npm install @usefy/use-focus-withinQuick start
import { useFocusWithin } from "@usefy/use-focus-within";
function ContactForm() {
const [ref, focused] = useFocusWithin<HTMLFormElement>();
return (
<form
ref={ref}
style={{ outline: focused ? "2px solid dodgerblue" : "none" }}
>
<input placeholder="Name" />
<input placeholder="Email" />
<button type="submit">Send</button>
</form>
);
}API reference
useFocusWithin(options?)
const [ref, focused] = useFocusWithin<T>(options?);
// <div ref={ref}> …focusable content… </div>
Returns a [ref, focused] tuple:
| Element | Type | Description |
|---|---|---|
ref | (node: T | null) => void | A stable callback ref to attach to the container element you want to track. |
focused | boolean | true whenever the active element is the container or any descendant. Starts false (also on the server). |
T defaults to HTMLElement; pass the concrete element type (e.g. HTMLFormElement) for a precisely-typed ref.
Options — UseFocusWithinOptions
| Option | Type | Description |
|---|---|---|
onFocus | (event: FocusEvent) => void | Fires when focus enters a previously-unfocused subtree. Receives the triggering focusin event. |
onBlur | (event: FocusEvent) => void | Fires when focus leaves the subtree entirely. Receives the triggering focusout event. |
Both callbacks fire only on the edge transitions — moving focus from one descendant to another fires neither.
Also exported
isFocusInside(container, target)— the reusable predicate for "is this node the container or a descendant of it" (folds in thenullcases).- Types:
UseFocusWithinOptions,UseFocusWithinRef,UseFocusWithinReturn.
How focusout is resolved
On focusout the hook decides whether focus truly left the subtree:
- If
event.relatedTargetis a node inside the container → focus just moved between descendants; stayfocused. - If
relatedTargetis a node outside the container → focus left; gofalse. - If
relatedTargetisnull(unreliable — reported when focus goes to nothing, to another window, or by browsers that omit it) → defer one microtask and re-checkdocument.activeElement; gofalseonly if focus genuinely ended up outside.
Step 3 is what makes the hook robust to the well-known null-relatedTarget quirk while staying fully testable in jsdom.
Go deeper
This page is the quick reference. For every example, prop, and edge case, read the full README — or open Storybook to change props live.