0
# Focus Control Hooks
1
2
React hooks for programmatic focus management, providing fine-grained control over focus behavior within and outside of focus locks. These hooks enable advanced focus manipulation scenarios and focus state tracking.
3
4
## Capabilities
5
6
### useFocusScope Hook
7
8
Returns FocusControl over the current FocusLock. Can only be used within a FocusLock component.
9
10
```typescript { .api }
11
/**
12
* Returns FocusControl over the current FocusLock
13
* Can only be used within FocusLock component
14
* @returns FocusControl interface for managing focus
15
*/
16
function useFocusScope(): FocusControl;
17
18
interface FocusControl {
19
/** Move focus to the current scope, acts as autofocus */
20
autoFocus(): Promise<void>;
21
22
/** Focus the next element in the scope */
23
focusNext(options?: FocusOptions): Promise<void>;
24
25
/** Focus the previous element in the scope */
26
focusPrev(options?: FocusOptions): Promise<void>;
27
28
/** Focus the first element in the scope */
29
focusFirst(options?: Pick<FocusOptions, 'onlyTabbable'>): Promise<void>;
30
31
/** Focus the last element in the scope */
32
focusLast(options?: Pick<FocusOptions, 'onlyTabbable'>): Promise<void>;
33
}
34
35
interface FocusOptions {
36
/** Enable focus cycle, default: true */
37
cycle?: boolean;
38
39
/** Limit to tabbable elements only, default: true */
40
onlyTabbable?: boolean;
41
}
42
```
43
44
**Usage Example:**
45
46
```typescript
47
import React from "react";
48
import FocusLock, { useFocusScope } from "react-focus-lock";
49
50
function NavigableModal() {
51
const focusScope = useFocusScope();
52
53
const handleKeyDown = async (event: React.KeyboardEvent) => {
54
switch (event.key) {
55
case 'ArrowDown':
56
event.preventDefault();
57
await focusScope.focusNext();
58
break;
59
case 'ArrowUp':
60
event.preventDefault();
61
await focusScope.focusPrev();
62
break;
63
case 'Home':
64
event.preventDefault();
65
await focusScope.focusFirst();
66
break;
67
case 'End':
68
event.preventDefault();
69
await focusScope.focusLast();
70
break;
71
}
72
};
73
74
return (
75
<div onKeyDown={handleKeyDown}>
76
<input placeholder="First input" />
77
<button>Middle button</button>
78
<input placeholder="Last input" />
79
</div>
80
);
81
}
82
83
function App() {
84
return (
85
<FocusLock>
86
<NavigableModal />
87
</FocusLock>
88
);
89
}
90
```
91
92
### useFocusController Hook
93
94
Returns FocusControl over given elements. Can be used outside of FocusLock and accepts HTML elements or React refs.
95
96
```typescript { .api }
97
/**
98
* Returns FocusControl over given elements
99
* Can be used outside of FocusLock
100
* @param shards - HTML elements or React refs to control
101
* @returns FocusControl interface for managing focus
102
*/
103
function useFocusController<Elements extends HTMLElement = HTMLElement>(
104
...shards: ReadonlyArray<HTMLElement | { current: HTMLElement | null }>
105
): FocusControl;
106
```
107
108
**Usage Example:**
109
110
```typescript
111
import React, { useRef } from "react";
112
import { useFocusController } from "react-focus-lock";
113
114
function CustomFocusController() {
115
const firstRef = useRef<HTMLInputElement>(null);
116
const secondRef = useRef<HTMLButtonElement>(null);
117
const thirdRef = useRef<HTMLInputElement>(null);
118
119
const focusController = useFocusController(firstRef, secondRef, thirdRef);
120
121
const handleNext = async () => {
122
await focusController.focusNext();
123
};
124
125
const handlePrevious = async () => {
126
await focusController.focusPrev();
127
};
128
129
const handleAutoFocus = async () => {
130
await focusController.autoFocus();
131
};
132
133
return (
134
<div>
135
<input ref={firstRef} placeholder="First controlled element" />
136
<button ref={secondRef}>Second controlled element</button>
137
<input ref={thirdRef} placeholder="Third controlled element" />
138
139
<div>
140
<button onClick={handleAutoFocus}>Auto Focus</button>
141
<button onClick={handleNext}>Focus Next</button>
142
<button onClick={handlePrevious}>Focus Previous</button>
143
</div>
144
</div>
145
);
146
}
147
```
148
149
### useFocusState Hook
150
151
Returns focus state information for a given node, tracking whether the node or its children have focus.
152
153
```typescript { .api }
154
/**
155
* Returns focus state information for a given node
156
* @param callbacks - Optional focus/blur callbacks
157
* @returns Object with focus state and handlers
158
*/
159
function useFocusState<T extends Element>(callbacks?: FocusCallbacks): {
160
/** Whether currently focused or focus is inside */
161
active: boolean;
162
163
/** Focus state string indicating type of focus relationship */
164
state: string;
165
166
/** Focus handler to be passed to the node */
167
onFocus: FocusEventHandler<T>;
168
169
/** Reference to the node */
170
ref: RefObject<T>;
171
};
172
173
interface FocusCallbacks {
174
/** Called when focus enters the node */
175
onFocus(): void;
176
177
/** Called when focus leaves the node */
178
onBlur(): void;
179
}
180
```
181
182
**Usage Example:**
183
184
```typescript
185
import React from "react";
186
import { useFocusState } from "react-focus-lock";
187
188
function FocusTracker() {
189
const { active, state, ref, onFocus } = useFocusState<HTMLDivElement>({
190
onFocus: () => console.log("Focus entered"),
191
onBlur: () => console.log("Focus left")
192
});
193
194
return (
195
<div ref={ref} onFocus={onFocus} className={active ? "focused" : "not-focused"}>
196
<h3>Focus Status: {active ? "Focused" : "Not Focused"}</h3>
197
<p>Focus State: {state}</p>
198
<input placeholder="Input inside tracker" />
199
<button>Button inside tracker</button>
200
</div>
201
);
202
}
203
```
204
205
### useFocusInside Hook
206
207
Moves focus inside a given node reference.
208
209
```typescript { .api }
210
/**
211
* Moves focus inside a given node reference
212
* @param node - React ref pointing to the target node
213
*/
214
function useFocusInside(node: RefObject<HTMLElement>): void;
215
```
216
217
**Usage Example:**
218
219
```typescript
220
import React, { useRef, useEffect } from "react";
221
import { useFocusInside } from "react-focus-lock";
222
223
function AutoFocusContainer() {
224
const containerRef = useRef<HTMLDivElement>(null);
225
226
// This will move focus inside the container on mount
227
useFocusInside(containerRef);
228
229
return (
230
<div ref={containerRef}>
231
<h3>Focus will move here</h3>
232
<input placeholder="This might get focused" />
233
<button>Or this button</button>
234
</div>
235
);
236
}
237
238
function ConditionalFocus({ shouldFocus }: { shouldFocus: boolean }) {
239
const containerRef = useRef<HTMLDivElement>(null);
240
241
useEffect(() => {
242
if (shouldFocus && containerRef.current) {
243
// Manually trigger focus inside
244
useFocusInside(containerRef);
245
}
246
}, [shouldFocus]);
247
248
return (
249
<div ref={containerRef}>
250
<input placeholder="Conditional focus target" />
251
<button>Another target</button>
252
</div>
253
);
254
}
255
```