or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

focus-navigation.mdfocus-ring.mdfocus-scope.mdindex.mdvirtual-focus.md

focus-scope.mddocs/

0

# Focus Scope Management

1

2

Focus scope management provides focus containment, restoration, and programmatic navigation for modal dialogs, popovers, and other overlay interfaces that need to trap focus within a specific area.

3

4

## Capabilities

5

6

### FocusScope Component

7

8

A React component that manages focus for its descendants, supporting focus containment, restoration, and auto-focusing.

9

10

```typescript { .api }

11

/**

12

* FocusScope manages focus for its descendants. It supports containing focus inside

13

* the scope, restoring focus to the previously focused element on unmount, and auto

14

* focusing children on mount. It also acts as a container for a programmatic focus

15

* management interface.

16

*/

17

function FocusScope(props: FocusScopeProps): JSX.Element;

18

19

interface FocusScopeProps {

20

/** The contents of the focus scope. */

21

children: ReactNode;

22

23

/**

24

* Whether to contain focus inside the scope, so users cannot

25

* move focus outside, for example in a modal dialog.

26

*/

27

contain?: boolean;

28

29

/**

30

* Whether to restore focus back to the element that was focused

31

* when the focus scope mounted, after the focus scope unmounts.

32

*/

33

restoreFocus?: boolean;

34

35

/** Whether to auto focus the first focusable element in the focus scope on mount. */

36

autoFocus?: boolean;

37

}

38

```

39

40

**Usage Examples:**

41

42

```typescript

43

import React, { useState } from "react";

44

import { FocusScope } from "@react-aria/focus";

45

46

// Modal dialog with focus containment

47

function Modal({ isOpen, onClose, title, children }) {

48

if (!isOpen) return null;

49

50

return (

51

<div className="modal-backdrop" onClick={onClose}>

52

<FocusScope contain restoreFocus autoFocus>

53

<div

54

className="modal-content"

55

onClick={(e) => e.stopPropagation()}

56

role="dialog"

57

aria-modal="true"

58

aria-labelledby="modal-title"

59

>

60

<h2 id="modal-title">{title}</h2>

61

{children}

62

<button onClick={onClose}>Close</button>

63

</div>

64

</FocusScope>

65

</div>

66

);

67

}

68

69

// Popover without focus containment but with restoration

70

function Popover({ isOpen, children }) {

71

if (!isOpen) return null;

72

73

return (

74

<FocusScope restoreFocus>

75

<div className="popover">

76

{children}

77

</div>

78

</FocusScope>

79

);

80

}

81

82

// Auto-focus form

83

function AutoFocusForm({ children }) {

84

return (

85

<FocusScope autoFocus>

86

<form>

87

<input type="text" placeholder="This will be auto-focused" />

88

{children}

89

</form>

90

</FocusScope>

91

);

92

}

93

```

94

95

### useFocusManager Hook

96

97

Returns a FocusManager interface for programmatic focus movement within the nearest FocusScope.

98

99

```typescript { .api }

100

/**

101

* Returns a FocusManager interface for the parent FocusScope.

102

* A FocusManager can be used to programmatically move focus within

103

* a FocusScope, e.g. in response to user events like keyboard navigation.

104

*/

105

function useFocusManager(): FocusManager | undefined;

106

107

interface FocusManager {

108

/** Moves focus to the next focusable or tabbable element in the focus scope. */

109

focusNext(opts?: FocusManagerOptions): FocusableElement | null;

110

/** Moves focus to the previous focusable or tabbable element in the focus scope. */

111

focusPrevious(opts?: FocusManagerOptions): FocusableElement | null;

112

/** Moves focus to the first focusable or tabbable element in the focus scope. */

113

focusFirst(opts?: FocusManagerOptions): FocusableElement | null;

114

/** Moves focus to the last focusable or tabbable element in the focus scope. */

115

focusLast(opts?: FocusManagerOptions): FocusableElement | null;

116

}

117

118

interface FocusManagerOptions {

119

/** The element to start searching from. The currently focused element by default. */

120

from?: Element;

121

/** Whether to only include tabbable elements, or all focusable elements. */

122

tabbable?: boolean;

123

/** Whether focus should wrap around when it reaches the end of the scope. */

124

wrap?: boolean;

125

/** A callback that determines whether the given element is focused. */

126

accept?: (node: Element) => boolean;

127

}

128

```

129

130

**Usage Examples:**

131

132

```typescript

133

import React from "react";

134

import { FocusScope, useFocusManager } from "@react-aria/focus";

135

136

// Arrow key navigation in a list

137

function NavigableList({ items }) {

138

const focusManager = useFocusManager();

139

140

const handleKeyDown = (e: React.KeyboardEvent) => {

141

switch (e.key) {

142

case 'ArrowDown':

143

e.preventDefault();

144

focusManager?.focusNext({ wrap: true });

145

break;

146

case 'ArrowUp':

147

e.preventDefault();

148

focusManager?.focusPrevious({ wrap: true });

149

break;

150

case 'Home':

151

e.preventDefault();

152

focusManager?.focusFirst();

153

break;

154

case 'End':

155

e.preventDefault();

156

focusManager?.focusLast();

157

break;

158

}

159

};

160

161

return (

162

<FocusScope>

163

<div

164

role="listbox"

165

onKeyDown={handleKeyDown}

166

tabIndex={0}

167

>

168

{items.map((item, index) => (

169

<div key={index} role="option" tabIndex={-1}>

170

{item}

171

</div>

172

))}

173

</div>

174

</FocusScope>

175

);

176

}

177

178

// Custom navigation with filtering

179

function FilteredNavigableGrid({ items, isEnabled }) {

180

const focusManager = useFocusManager();

181

182

const focusNextEnabled = () => {

183

focusManager?.focusNext({

184

tabbable: true,

185

wrap: true,

186

accept: (node) => {

187

// Only focus enabled items

188

return !node.hasAttribute('data-disabled');

189

}

190

});

191

};

192

193

return (

194

<FocusScope>

195

<div className="grid" onKeyDown={handleKeyDown}>

196

{items.map((item, index) => (

197

<button

198

key={index}

199

data-disabled={!isEnabled(item) || undefined}

200

disabled={!isEnabled(item)}

201

>

202

{item.name}

203

</button>

204

))}

205

</div>

206

</FocusScope>

207

);

208

}

209

```

210

211

### Element Scope Detection

212

213

Utility for detecting if an element is within a child scope of the currently active scope.

214

215

```typescript { .api }

216

/**

217

* Checks if element is in child of active scope.

218

* @private - Internal utility, may change without notice

219

*/

220

function isElementInChildOfActiveScope(element: Element): boolean;

221

```

222

223

This function is primarily used internally by React Aria components but is exported for advanced use cases where you need to detect focus scope containment.

224

225

## Focus Scope Behavior

226

227

### Focus Containment

228

229

When `contain={true}`:

230

- Tab and Shift+Tab navigation is trapped within the scope

231

- Focus cannot escape the scope boundaries via keyboard navigation

232

- If focus moves outside programmatically, it's automatically returned to the scope

233

- Useful for modal dialogs and other overlay interfaces

234

235

### Focus Restoration

236

237

When `restoreFocus={true}`:

238

- The previously focused element is remembered when the scope mounts

239

- Focus is restored to that element when the scope unmounts

240

- Handles cases where the original element is no longer in the DOM

241

- Respects focus scope hierarchy for nested scopes

242

243

### Auto Focus

244

245

When `autoFocus={true}`:

246

- Automatically focuses the first focusable/tabbable element when the scope mounts

247

- Does not move focus if an element within the scope is already focused

248

- Useful for dialogs and forms that should immediately be keyboard accessible

249

250

### Focus Scope Tree

251

252

React Aria maintains an internal tree of focus scopes to handle:

253

- Nested scope behavior and precedence

254

- Proper focus restoration in complex applications

255

- Child scope detection and focus containment rules

256

- Scope activation tracking for multiple overlapping scopes