or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

tessl/npm-react-stately--list

React state management hooks for list-like components with selection support

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@react-stately/list@3.13.x

To install, run

npx @tessl/cli install tessl/npm-react-stately--list@3.13.0

0

# @react-stately/list

1

2

React state management hooks for list-like components, providing accessible and performant state management for interactive list components including listboxes, menus, and data grids. This package handles complex list interactions including selection modes, disabled items, filtering, and collection management.

3

4

## Package Information

5

6

- **Package Name**: @react-stately/list

7

- **Package Type**: npm

8

- **Language**: TypeScript

9

- **Installation**: `npm install @react-stately/list`

10

11

## Core Imports

12

13

```typescript

14

import { useListState, useSingleSelectListState, ListCollection, UNSTABLE_useFilteredListState } from "@react-stately/list";

15

import type { ListProps, ListState, SingleSelectListProps, SingleSelectListState } from "@react-stately/list";

16

```

17

18

CommonJS:

19

20

```javascript

21

const { useListState, useSingleSelectListState, ListCollection, UNSTABLE_useFilteredListState } = require("@react-stately/list");

22

```

23

24

## Basic Usage

25

26

```typescript

27

import { useListState } from "@react-stately/list";

28

import { Item } from "@react-stately/collections";

29

30

function MyListComponent() {

31

const state = useListState({

32

children: [

33

<Item key="apple">Apple</Item>,

34

<Item key="banana">Banana</Item>,

35

<Item key="orange">Orange</Item>

36

],

37

selectionMode: "multiple"

38

});

39

40

return (

41

<div>

42

{[...state.collection].map((item) => (

43

<div

44

key={item.key}

45

onClick={() => state.selectionManager.toggleSelection(item.key)}

46

>

47

{item.rendered}

48

</div>

49

))}

50

</div>

51

);

52

}

53

```

54

55

## Architecture

56

57

The package is built around several key components:

58

59

- **Collection Management**: `ListCollection` provides efficient item navigation and access methods

60

- **State Management**: Hooks that integrate with React Stately's selection system and collection utilities

61

- **Selection Handling**: Built-in support for multiple and single selection patterns with accessibility features

62

- **Focus Management**: Automatic focus handling when items are added/removed from collections

63

- **Filtering Support**: Optional filtering capabilities for dynamic list content

64

65

## Capabilities

66

67

### Multiple Selection Lists

68

69

Creates state for list components with multiple selection support, including complex selection scenarios with disabled items and custom filtering.

70

71

```typescript { .api }

72

/**

73

* Provides state management for list-like components. Handles building a collection

74

* of items from props, and manages multiple selection state.

75

*/

76

function useListState<T extends object>(props: ListProps<T>): ListState<T>;

77

78

interface ListProps<T> extends CollectionStateBase<T>, MultipleSelectionStateProps {

79

/** Filter function to generate a filtered list of nodes. */

80

filter?: (nodes: Iterable<Node<T>>) => Iterable<Node<T>>;

81

/** @private */

82

suppressTextValueWarning?: boolean;

83

/**

84

* A delegate object that provides layout information for items in the collection.

85

* This can be used to override the behavior of shift selection.

86

*/

87

layoutDelegate?: LayoutDelegate;

88

}

89

90

interface ListState<T> {

91

/** A collection of items in the list. */

92

collection: Collection<Node<T>>;

93

/** A set of items that are disabled. */

94

disabledKeys: Set<Key>;

95

/** A selection manager to read and update multiple selection state. */

96

selectionManager: SelectionManager;

97

}

98

```

99

100

### Single Selection Lists

101

102

Creates state for list components with single selection, providing a simplified interface for components that only need single selection.

103

104

```typescript { .api }

105

/**

106

* Provides state management for list-like components with single selection.

107

* Handles building a collection of items from props, and manages selection state.

108

*/

109

function useSingleSelectListState<T extends object>(props: SingleSelectListProps<T>): SingleSelectListState<T>;

110

111

interface SingleSelectListProps<T> extends CollectionStateBase<T>, Omit<SingleSelection, 'disallowEmptySelection'> {

112

/** Filter function to generate a filtered list of nodes. */

113

filter?: (nodes: Iterable<Node<T>>) => Iterable<Node<T>>;

114

/** @private */

115

suppressTextValueWarning?: boolean;

116

}

117

118

interface SingleSelectListState<T> extends ListState<T> {

119

/** The key for the currently selected item. */

120

readonly selectedKey: Key | null;

121

/** Sets the selected key. */

122

setSelectedKey(key: Key | null): void;

123

/** The value of the currently selected item. */

124

readonly selectedItem: Node<T> | null;

125

}

126

```

127

128

### Filtered List State (Unstable)

129

130

Filters an existing collection using a provided filter function and returns a new ListState.

131

132

```typescript { .api }

133

/**

134

* Filters a collection using the provided filter function and returns a new ListState.

135

* @experimental This API is unstable and may change in future versions

136

*/

137

function UNSTABLE_useFilteredListState<T extends object>(

138

state: ListState<T>,

139

filterFn: ((nodeValue: string, node: Node<T>) => boolean) | null | undefined

140

): ListState<T>;

141

```

142

143

### Collection Implementation

144

145

Collection class that provides efficient navigation and item access for list components.

146

147

```typescript { .api }

148

/**

149

* Collection implementation for list components with navigation and item access methods

150

*/

151

class ListCollection<T> implements Collection<Node<T>> {

152

constructor(nodes: Iterable<Node<T>>);

153

154

/** Iterator over all nodes in the collection */

155

[Symbol.iterator](): IterableIterator<Node<T>>;

156

157

/** Returns the number of items in the collection */

158

get size(): number;

159

160

/** Returns an iterator of all keys in the collection */

161

getKeys(): IterableIterator<Key>;

162

163

/** Returns the key that comes before the given key */

164

getKeyBefore(key: Key): Key | null;

165

166

/** Returns the key that comes after the given key */

167

getKeyAfter(key: Key): Key | null;

168

169

/** Returns the first key in the collection */

170

getFirstKey(): Key | null;

171

172

/** Returns the last key in the collection */

173

getLastKey(): Key | null;

174

175

/** Returns the node for the given key */

176

getItem(key: Key): Node<T> | null;

177

178

/** Returns the node at the given index */

179

at(idx: number): Node<T> | null;

180

181

/** Returns the children of the given key */

182

getChildren(key: Key): Iterable<Node<T>>;

183

}

184

```

185

186

## Types

187

188

### Core Types

189

190

```typescript { .api }

191

// Re-exported from @react-types/shared and React

192

type Key = string | number;

193

type ReactNode = React.ReactNode;

194

type ReactElement = React.ReactElement;

195

196

interface Node<T> {

197

/** The type of item this node represents */

198

type: string;

199

/** A unique key for the node */

200

key: Key;

201

/** The object value the node was created from */

202

value: T | null;

203

/** The level of depth this node is at in the hierarchy */

204

level: number;

205

/** Whether this item has children, even if not loaded yet */

206

hasChildNodes: boolean;

207

/** The loaded children of this node (deprecated: Use collection.getChildren(node.key) instead) */

208

childNodes: Iterable<Node<T>>;

209

/** The rendered contents of this node (e.g. JSX) */

210

rendered: ReactNode;

211

/** A string value for this node, used for features like typeahead */

212

textValue: string;

213

/** An accessibility label for this node */

214

'aria-label'?: string;

215

/** The index of this node within its parent */

216

index: number;

217

/** A function that should be called to wrap the rendered node */

218

wrapper?: (element: ReactElement) => ReactElement;

219

/** The key of the parent node */

220

parentKey?: Key | null;

221

/** The key of the node before this node */

222

prevKey?: Key | null;

223

/** The key of the node after this node */

224

nextKey?: Key | null;

225

/** Additional properties specific to a particular node type */

226

props?: any;

227

/** @private */

228

shouldInvalidate?: (context: any) => boolean;

229

/** A function that renders this node to a React Element in the DOM */

230

render?: (node: Node<any>) => ReactElement;

231

}

232

233

interface Collection<T> {

234

/** The number of items in the collection */

235

readonly size: number;

236

/** Returns an iterator of all keys in the collection */

237

getKeys(): IterableIterator<Key>;

238

/** Returns the item for the given key */

239

getItem(key: Key): T | null;

240

/** Returns the item at the given index */

241

at(idx: number): T | null;

242

/** Returns the key that comes before the given key */

243

getKeyBefore(key: Key): Key | null;

244

/** Returns the key that comes after the given key */

245

getKeyAfter(key: Key): Key | null;

246

/** Returns the first key in the collection */

247

getFirstKey(): Key | null;

248

/** Returns the last key in the collection */

249

getLastKey(): Key | null;

250

/** Returns the children of the given key */

251

getChildren?(key: Key): Iterable<T>;

252

/** Returns the text value for the given key */

253

getTextValue?(key: Key): string;

254

/** Filters the collection using the provided filter function */

255

filter?(filterFn: (nodeValue: string, node: T) => boolean): Collection<T>;

256

}

257

258

interface CollectionStateBase<T> {

259

/** The contents of the collection */

260

children: ReactNode;

261

/** A list of keys to disable */

262

disabledKeys?: Key[];

263

}

264

265

interface MultipleSelectionStateProps {

266

/** The type of selection mode */

267

selectionMode?: SelectionMode;

268

/** The selection behavior for the collection */

269

selectionBehavior?: SelectionBehavior;

270

/** Whether empty selection is allowed */

271

disallowEmptySelection?: boolean;

272

/** The currently selected keys */

273

selectedKeys?: Selection;

274

/** The default selected keys (uncontrolled) */

275

defaultSelectedKeys?: Selection;

276

/** Handler called when the selection changes */

277

onSelectionChange?: (keys: Selection) => void;

278

/** The disabled keys in the collection */

279

disabledKeys?: Key[];

280

/** Whether disabledKeys applies to selection, actions, or both */

281

disabledBehavior?: DisabledBehavior;

282

}

283

284

interface SingleSelection {

285

/** The currently selected key */

286

selectedKey?: Key | null;

287

/** The default selected key (uncontrolled) */

288

defaultSelectedKey?: Key;

289

/** Handler called when the selection changes */

290

onSelectionChange?: (key: Key | null) => void;

291

}

292

293

interface LayoutDelegate {

294

/** Returns the key that should be selected when the user presses shift+arrow */

295

getKeyAbove?(key: Key): Key | null;

296

/** Returns the key that should be selected when the user presses shift+arrow */

297

getKeyBelow?(key: Key): Key | null;

298

/** Returns the key that should be selected when the user presses shift+arrow */

299

getKeyLeftOf?(key: Key): Key | null;

300

/** Returns the key that should be selected when the user presses shift+arrow */

301

getKeyRightOf?(key: Key): Key | null;

302

}

303

304

interface SelectionManager {

305

/** The type of selection that is allowed in the collection */

306

readonly selectionMode: SelectionMode;

307

/** The selection behavior for the collection */

308

readonly selectionBehavior: SelectionBehavior;

309

/** Whether the collection allows empty selection */

310

readonly disallowEmptySelection?: boolean;

311

/** Whether the collection is currently focused */

312

readonly isFocused: boolean;

313

/** The current focused key in the collection */

314

readonly focusedKey: Key | null;

315

/** Whether the first or last child of the focused key should receive focus */

316

readonly childFocusStrategy: FocusStrategy | null;

317

/** The currently selected keys in the collection */

318

readonly selectedKeys: Set<Key>;

319

/** Whether the selection is empty */

320

readonly isEmpty: boolean;

321

/** Whether all items in the collection are selected */

322

readonly isSelectAll: boolean;

323

/** The first selected key in the collection */

324

readonly firstSelectedKey: Key | null;

325

/** The last selected key in the collection */

326

readonly lastSelectedKey: Key | null;

327

/** The currently disabled keys in the collection */

328

readonly disabledKeys: Set<Key>;

329

/** Whether disabledKeys applies to selection, actions, or both */

330

readonly disabledBehavior: DisabledBehavior;

331

/** The collection of nodes that the selection manager handles */

332

collection: Collection<Node<unknown>>;

333

334

/** Sets whether the collection is focused */

335

setFocused(isFocused: boolean): void;

336

/** Sets the focused key, and optionally, whether the first or last child of that key should receive focus */

337

setFocusedKey(key: Key | null, child?: FocusStrategy): void;

338

/** Returns whether a key is selected */

339

isSelected(key: Key): boolean;

340

/** Returns whether the current selection is equal to the given selection */

341

isSelectionEqual(selection: Set<Key>): boolean;

342

/** Extends the selection to the given key */

343

extendSelection(toKey: Key): void;

344

/** Toggles whether the given key is selected */

345

toggleSelection(key: Key): void;

346

/** Replaces the selection with only the given key */

347

replaceSelection(key: Key): void;

348

/** Replaces the selection with the given keys */

349

setSelectedKeys(keys: Iterable<Key>): void;

350

/** Selects all items in the collection */

351

selectAll(): void;

352

/** Removes all keys from the selection */

353

clearSelection(): void;

354

/** Toggles between select all and an empty selection */

355

toggleSelectAll(): void;

356

/** Toggles, replaces, or extends selection to the given key depending on the pointer event and collection's selection mode */

357

select(key: Key, e?: PressEvent | LongPressEvent | PointerEvent): void;

358

/** Returns whether the given key can be selected */

359

canSelectItem(key: Key): boolean;

360

/** Returns whether the given key is non-interactive, i.e. both selection and actions are disabled */

361

isDisabled(key: Key): boolean;

362

/** Sets the selection behavior for the collection */

363

setSelectionBehavior(selectionBehavior: SelectionBehavior): void;

364

/** Returns whether the given key is a hyperlink */

365

isLink(key: Key): boolean;

366

/** Returns the props for the given item */

367

getItemProps(key: Key): any;

368

}

369

370

type SelectionMode = 'none' | 'single' | 'multiple';

371

type Selection = 'all' | Set<Key>;

372

type SelectionBehavior = 'toggle' | 'replace';

373

type DisabledBehavior = 'selection' | 'all';

374

type FocusStrategy = 'first' | 'last';

375

```

376

377

## Usage Examples

378

379

### Multiple Selection with Filtering

380

381

```typescript

382

import { useListState } from "@react-stately/list";

383

import { Item } from "@react-stately/collections";

384

385

const fruits = [

386

{ id: "1", name: "Apple", color: "red" },

387

{ id: "2", name: "Banana", color: "yellow" },

388

{ id: "3", name: "Orange", color: "orange" },

389

{ id: "4", name: "Grape", color: "purple" }

390

];

391

392

function FilterableList() {

393

const [searchTerm, setSearchTerm] = useState("");

394

395

const state = useListState({

396

children: fruits.map(fruit => (

397

<Item key={fruit.id} textValue={fruit.name}>

398

{fruit.name} ({fruit.color})

399

</Item>

400

)),

401

selectionMode: "multiple",

402

filter: searchTerm

403

? (nodes) => [...nodes].filter(node =>

404

node.textValue.toLowerCase().includes(searchTerm.toLowerCase())

405

)

406

: undefined

407

});

408

409

return (

410

<div>

411

<input

412

type="text"

413

placeholder="Search fruits..."

414

onChange={(e) => setSearchTerm(e.target.value)}

415

/>

416

<div>

417

{[...state.collection].map((item) => (

418

<div

419

key={item.key}

420

onClick={() => state.selectionManager.toggleSelection(item.key)}

421

style={{

422

backgroundColor: state.selectionManager.isSelected(item.key) ? "#e3f2fd" : "white"

423

}}

424

>

425

{item.rendered}

426

</div>

427

))}

428

</div>

429

</div>

430

);

431

}

432

```

433

434

### Single Selection with Controlled State

435

436

```typescript

437

import { useSingleSelectListState } from "@react-stately/list";

438

import { Item } from "@react-stately/collections";

439

440

function SingleSelectList() {

441

const [selectedKey, setSelectedKey] = useState<Key | null>("1");

442

443

const state = useSingleSelectListState({

444

children: [

445

<Item key="1">Option 1</Item>,

446

<Item key="2">Option 2</Item>,

447

<Item key="3">Option 3</Item>

448

],

449

selectedKey,

450

onSelectionChange: setSelectedKey

451

});

452

453

return (

454

<div>

455

<p>Selected: {state.selectedItem?.rendered || "None"}</p>

456

<div>

457

{[...state.collection].map((item) => (

458

<button

459

key={item.key}

460

onClick={() => state.setSelectedKey(item.key)}

461

style={{

462

backgroundColor: item.key === state.selectedKey ? "#1976d2" : "#f5f5f5",

463

color: item.key === state.selectedKey ? "white" : "black"

464

}}

465

>

466

{item.rendered}

467

</button>

468

))}

469

</div>

470

</div>

471

);

472

}

473

```

474

475

### Working with Disabled Items

476

477

```typescript

478

import { useListState } from "@react-stately/list";

479

import { Item } from "@react-stately/collections";

480

481

function ListWithDisabledItems() {

482

const state = useListState({

483

children: [

484

<Item key="available1">Available Item 1</Item>,

485

<Item key="disabled1">Disabled Item 1</Item>,

486

<Item key="available2">Available Item 2</Item>,

487

<Item key="disabled2">Disabled Item 2</Item>

488

],

489

disabledKeys: ["disabled1", "disabled2"],

490

selectionMode: "multiple"

491

});

492

493

return (

494

<div>

495

{[...state.collection].map((item) => {

496

const isDisabled = state.disabledKeys.has(item.key);

497

return (

498

<div

499

key={item.key}

500

onClick={!isDisabled ? () => state.selectionManager.toggleSelection(item.key) : undefined}

501

style={{

502

opacity: isDisabled ? 0.5 : 1,

503

cursor: isDisabled ? "not-allowed" : "pointer",

504

backgroundColor: state.selectionManager.isSelected(item.key) ? "#e3f2fd" : "white"

505

}}

506

>

507

{item.rendered}

508

</div>

509

);

510

})}

511

</div>

512

);

513

}

514

```