or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

date-time.mddrag-drop.mdfocus-management.mdform-controls.mdindex.mdinteractions.mdinternationalization.mdlayout-navigation.mdoverlays-modals.mdselection-controls.mdtags.mdtoast-notifications.mdutilities.md

utilities.mddocs/

0

# Utilities

1

2

Utility hooks and functions for common React Aria patterns, prop management, ID generation, and routing integration. These utilities provide foundational functionality used throughout the React Aria ecosystem.

3

4

## Capabilities

5

6

### Prop Management

7

8

Utilities for merging and managing component props safely.

9

10

```typescript { .api }

11

/**

12

* Merges multiple props objects, handling event handlers and class names properly

13

* @param props - Props objects to merge

14

* @returns Merged props object

15

*/

16

function mergeProps(...props: any[]): any;

17

```

18

19

**Usage Examples:**

20

21

```typescript

22

import { mergeProps } from "react-aria";

23

24

// Merge props from multiple sources

25

function CustomButton(props) {

26

const baseProps = {

27

className: 'btn',

28

onClick: () => console.log('Base click')

29

};

30

31

const userProps = props;

32

33

const { buttonProps } = useButton(props, ref);

34

35

// Safely merge all props

36

const mergedProps = mergeProps(baseProps, buttonProps, userProps);

37

38

return <button {...mergedProps} ref={ref} />;

39

}

40

41

// Merge event handlers

42

function EventHandlingComponent(props) {

43

const internalHandler = (e) => {

44

console.log('Internal handler');

45

// Internal logic

46

};

47

48

const mergedProps = mergeProps(

49

{ onClick: internalHandler },

50

{ onClick: props.onClick }

51

);

52

53

// Both handlers will be called

54

return <button {...mergedProps}>Click me</button>;

55

}

56

57

// Merge class names

58

function StyledComponent(props) {

59

const defaultProps = { className: 'default-class' };

60

const stateProps = { className: props.isActive ? 'active' : '' };

61

const userProps = { className: props.className };

62

63

const merged = mergeProps(defaultProps, stateProps, userProps);

64

// className will be: 'default-class active user-class'

65

66

return <div {...merged}>{props.children}</div>;

67

}

68

```

69

70

### Function Chaining

71

72

Utility for chaining multiple callback functions safely.

73

74

```typescript { .api }

75

/**

76

* Chains multiple callback functions into a single function

77

* @param callbacks - Functions to chain together

78

* @returns Chained function that calls all callbacks

79

*/

80

function chain(...callbacks: any[]): (...args: any[]) => void;

81

```

82

83

**Usage Examples:**

84

85

```typescript

86

import { chain } from "react-aria";

87

88

// Chain multiple event handlers

89

function MultiHandlerButton(props) {

90

const handleClick = (e) => {

91

console.log('Button clicked');

92

};

93

94

const handleAnalytics = (e) => {

95

analytics.track('button_click');

96

};

97

98

// Chain handlers together

99

const chainedHandler = chain(handleClick, handleAnalytics, props.onClick);

100

101

return <button onClick={chainedHandler}>Click me</button>;

102

}

103

104

// Chain with conditional handlers

105

function ConditionalChain(props) {

106

const baseHandler = () => console.log('Base');

107

108

const conditionalHandler = props.debug ?

109

() => console.log('Debug mode') :

110

null;

111

112

// chain ignores null/undefined functions

113

const handler = chain(baseHandler, conditionalHandler, props.onAction);

114

115

return <button onClick={handler}>Action</button>;

116

}

117

118

// Chain cleanup functions

119

function useMultipleEffects() {

120

useEffect(() => {

121

const cleanup1 = setupEffect1();

122

const cleanup2 = setupEffect2();

123

const cleanup3 = setupEffect3();

124

125

// Chain all cleanup functions

126

return chain(cleanup1, cleanup2, cleanup3);

127

}, []);

128

}

129

```

130

131

### ID Generation

132

133

Utilities for generating unique IDs for accessibility and component relationships.

134

135

```typescript { .api }

136

/**

137

* Generates a unique ID, with optional prefix

138

* @param defaultId - Default ID to use if provided

139

* @returns Unique ID string

140

*/

141

function useId(defaultId?: string): string;

142

```

143

144

**Usage Examples:**

145

146

```typescript

147

import { useId } from "react-aria";

148

149

// Generate unique IDs for form fields

150

function FormField({ label, children, id: userProvidedId }) {

151

const id = useId(userProvidedId);

152

const descriptionId = useId();

153

154

return (

155

<div>

156

<label htmlFor={id}>{label}</label>

157

{React.cloneElement(children, {

158

id,

159

'aria-describedby': descriptionId

160

})}

161

<div id={descriptionId} className="field-description">

162

Additional help text

163

</div>

164

</div>

165

);

166

}

167

168

// Generate IDs for complex components

169

function TabsComponent({ tabs }) {

170

const tabListId = useId();

171

172

return (

173

<div>

174

<div role="tablist" id={tabListId}>

175

{tabs.map((tab, index) => {

176

const tabId = useId();

177

const panelId = useId();

178

179

return (

180

<button

181

key={index}

182

role="tab"

183

id={tabId}

184

aria-controls={panelId}

185

>

186

{tab.title}

187

</button>

188

);

189

})}

190

</div>

191

192

{tabs.map((tab, index) => {

193

const panelId = useId();

194

const tabId = useId();

195

196

return (

197

<div

198

key={index}

199

role="tabpanel"

200

id={panelId}

201

aria-labelledby={tabId}

202

>

203

{tab.content}

204

</div>

205

);

206

})}

207

</div>

208

);

209

}

210

211

// Use provided ID or generate one

212

function AccessibleComponent({ id, label, children }) {

213

const componentId = useId(id);

214

const labelId = useId();

215

216

return (

217

<div>

218

<div id={labelId}>{label}</div>

219

<div

220

id={componentId}

221

aria-labelledby={labelId}

222

>

223

{children}

224

</div>

225

</div>

226

);

227

}

228

```

229

230

### Object Refs

231

232

Utility for creating object refs that can be used with forwarded refs.

233

234

```typescript { .api }

235

/**

236

* Creates an object ref that can be used with forwardRef

237

* @param forwardedRef - Forwarded ref from parent component

238

* @returns Object ref that can be used in hooks

239

*/

240

function useObjectRef<T>(forwardedRef: ForwardedRef<T>): RefObject<T>;

241

```

242

243

**Usage Examples:**

244

245

```typescript

246

import React, { forwardRef } from "react";

247

import { useObjectRef, useButton } from "react-aria";

248

249

// Forward refs properly in custom components

250

const CustomButton = forwardRef<HTMLButtonElement, ButtonProps>((props, forwardedRef) => {

251

const ref = useObjectRef(forwardedRef);

252

const { buttonProps } = useButton(props, ref);

253

254

return (

255

<button {...buttonProps} ref={ref}>

256

{props.children}

257

</button>

258

);

259

});

260

261

// Use with multiple hooks that need the same ref

262

const ComplexInput = forwardRef<HTMLInputElement, InputProps>((props, forwardedRef) => {

263

const ref = useObjectRef(forwardedRef);

264

265

const { inputProps } = useTextField(props, ref);

266

const { focusProps } = useFocus({ onFocus: props.onFocus }, ref);

267

const { hoverProps } = useHover({ onHover: props.onHover }, ref);

268

269

const combinedProps = mergeProps(inputProps, focusProps, hoverProps);

270

271

return <input {...combinedProps} ref={ref} />;

272

});

273

274

// Imperative handle with object ref

275

const ImperativeComponent = forwardRef<ComponentHandle, ComponentProps>((props, forwardedRef) => {

276

const ref = useObjectRef<HTMLDivElement>(null);

277

278

useImperativeHandle(forwardedRef, () => ({

279

focus: () => ref.current?.focus(),

280

scrollIntoView: () => ref.current?.scrollIntoView(),

281

getBoundingClientRect: () => ref.current?.getBoundingClientRect()

282

}), []);

283

284

return <div ref={ref}>{props.children}</div>;

285

});

286

```

287

288

### Router Integration

289

290

Utilities for integrating React Aria with routing libraries.

291

292

```typescript { .api }

293

/**

294

* Router provider for React Aria components

295

* @param props - Router provider configuration

296

* @returns Provider component

297

*/

298

function RouterProvider(props: RouterProviderProps): JSX.Element;

299

300

interface RouterProviderProps {

301

/** Children to provide router context to */

302

children: ReactNode;

303

/** Navigate function from your router */

304

navigate: (path: string, options?: NavigateOptions) => void;

305

/** Current pathname */

306

pathname?: string;

307

/** Whether router uses hash routing */

308

useHref?: (href: string) => string;

309

}

310

311

interface NavigateOptions {

312

/** Whether to replace current history entry */

313

replace?: boolean;

314

/** State to pass with navigation */

315

state?: any;

316

}

317

```

318

319

**Usage Examples:**

320

321

```typescript

322

import { RouterProvider } from "react-aria";

323

import { useNavigate, useLocation } from "react-router-dom";

324

325

// React Router integration

326

function App() {

327

const navigate = useNavigate();

328

const location = useLocation();

329

330

return (

331

<RouterProvider

332

navigate={navigate}

333

pathname={location.pathname}

334

>

335

<YourApp />

336

</RouterProvider>

337

);

338

}

339

340

// Next.js integration

341

import { useRouter } from "next/router";

342

343

function MyApp({ Component, pageProps }) {

344

const router = useRouter();

345

346

return (

347

<RouterProvider

348

navigate={(path, options) => {

349

if (options?.replace) {

350

router.replace(path);

351

} else {

352

router.push(path);

353

}

354

}}

355

pathname={router.asPath}

356

>

357

<Component {...pageProps} />

358

</RouterProvider>

359

);

360

}

361

362

// Custom router integration

363

function CustomRouterApp() {

364

const [pathname, setPathname] = useState('/');

365

366

const navigate = useCallback((path, options) => {

367

if (options?.replace) {

368

history.replaceState(null, '', path);

369

} else {

370

history.pushState(null, '', path);

371

}

372

setPathname(path);

373

}, []);

374

375

return (

376

<RouterProvider

377

navigate={navigate}

378

pathname={pathname}

379

>

380

<YourApp />

381

</RouterProvider>

382

);

383

}

384

```

385

386

### SSR Support

387

388

Server-side rendering utilities and components.

389

390

```typescript { .api }

391

/**

392

* SSR provider component that handles hydration mismatches

393

* @param props - SSR provider configuration

394

* @returns Provider component

395

*/

396

function SSRProvider(props: SSRProviderProps): JSX.Element;

397

398

/**

399

* Hook to detect server-side rendering

400

* @returns Whether currently running on server

401

*/

402

function useIsSSR(): boolean;

403

404

interface SSRProviderProps {

405

/** Children to provide SSR context to */

406

children: ReactNode;

407

}

408

```

409

410

**Usage Examples:**

411

412

```typescript

413

import { SSRProvider, useIsSSR } from "react-aria";

414

415

// Wrap your app with SSRProvider

416

function App() {

417

return (

418

<SSRProvider>

419

<YourApp />

420

</SSRProvider>

421

);

422

}

423

424

// Conditional rendering based on SSR

425

function ClientOnlyComponent() {

426

const isSSR = useIsSSR();

427

428

if (isSSR) {

429

return <div>Loading...</div>;

430

}

431

432

return <InteractiveComponent />;

433

}

434

435

// Next.js pages/_app.js

436

export default function MyApp({ Component, pageProps }) {

437

return (

438

<SSRProvider>

439

<Component {...pageProps} />

440

</SSRProvider>

441

);

442

}

443

444

// Gatsby gatsby-browser.js and gatsby-ssr.js

445

export const wrapRootElement = ({ element }) => (

446

<SSRProvider>{element}</SSRProvider>

447

);

448

```

449

450

### Visually Hidden

451

452

Utilities for screen reader only content that is visually hidden but accessible to assistive technology.

453

454

```typescript { .api }

455

/**

456

* Component that renders content only for screen readers

457

* @param props - Visually hidden configuration

458

* @returns Visually hidden element

459

*/

460

function VisuallyHidden(props: VisuallyHiddenProps): JSX.Element;

461

462

/**

463

* Hook for visually hidden element behavior

464

* @param props - Visually hidden configuration

465

* @returns Visually hidden props

466

*/

467

function useVisuallyHidden(props?: VisuallyHiddenProps): VisuallyHiddenAria;

468

469

interface VisuallyHiddenProps {

470

/** Children content to hide visually */

471

children: ReactNode;

472

/** Element type to render */

473

elementType?: React.ElementType;

474

/** Whether to render children inside focusable element */

475

isFocusable?: boolean;

476

}

477

478

interface VisuallyHiddenAria {

479

/** Props for the visually hidden element */

480

visuallyHiddenProps: DOMAttributes<Element>;

481

}

482

```

483

484

**Usage Examples:**

485

486

```typescript

487

import { VisuallyHidden } from "react-aria";

488

489

// Screen reader only labels

490

function IconButton({ icon, label, ...props }) {

491

return (

492

<button {...props}>

493

<Icon>{icon}</Icon>

494

<VisuallyHidden>{label}</VisuallyHidden>

495

</button>

496

);

497

}

498

499

// Skip links for keyboard navigation

500

function SkipLinks() {

501

return (

502

<VisuallyHidden isFocusable>

503

<a href="#main-content">Skip to main content</a>

504

</VisuallyHidden>

505

);

506

}

507

508

// Accessible table headers

509

function DataTable() {

510

return (

511

<table>

512

<thead>

513

<tr>

514

<th>

515

Name

516

<VisuallyHidden>(sortable column)</VisuallyHidden>

517

</th>

518

<th>Actions</th>

519

</tr>

520

</thead>

521

</table>

522

);

523

}

524

```

525

526

### Development Utilities

527

528

Utilities for development and debugging.

529

530

```typescript { .api }

531

/**

532

* Development warning utility

533

* @param condition - Condition to check

534

* @param message - Warning message

535

*/

536

function warn(condition: boolean, message: string): void;

537

538

/**

539

* Development-only component prop validation

540

* @param props - Props to validate

541

* @param propTypes - Prop type definitions

542

* @param componentName - Name of component

543

*/

544

function validateProps(props: any, propTypes: any, componentName: string): void;

545

546

/**

547

* Get display name for debugging

548

* @param Component - React component

549

* @returns Display name string

550

*/

551

function getDisplayName(Component: React.ComponentType<any>): string;

552

```

553

554

**Usage Examples:**

555

556

```typescript

557

import { warn } from "react-aria";

558

559

// Development warnings

560

function CustomComponent(props) {

561

if (process.env.NODE_ENV !== 'production') {

562

warn(

563

!props.children && !props.label,

564

'CustomComponent should have either children or a label prop'

565

);

566

567

warn(

568

props.isDisabled && props.onPress,

569

'CustomComponent: onPress will not be called when isDisabled is true'

570

);

571

}

572

573

// Component implementation

574

}

575

576

// Debug component names

577

function withAriaLabel(Component) {

578

const WrappedComponent = (props) => {

579

return <Component {...props} />;

580

};

581

582

WrappedComponent.displayName = `withAriaLabel(${getDisplayName(Component)})`;

583

584

return WrappedComponent;

585

}

586

```

587

588

## Types and Constants

589

590

```typescript { .api }

591

type ForwardedRef<T> = ((instance: T | null) => void) | MutableRefObject<T | null> | null;

592

593

type RefObject<T> = { readonly current: T | null };

594

595

interface DOMAttributes<T = Element> {

596

// Event handlers

597

onFocus?: (e: FocusEvent<T>) => void;

598

onBlur?: (e: FocusEvent<T>) => void;

599

onClick?: (e: MouseEvent<T>) => void;

600

onKeyDown?: (e: KeyboardEvent<T>) => void;

601

onKeyUp?: (e: KeyboardEvent<T>) => void;

602

603

// ARIA attributes

604

'aria-label'?: string;

605

'aria-labelledby'?: string;

606

'aria-describedby'?: string;

607

'aria-expanded'?: boolean;

608

'aria-selected'?: boolean;

609

'aria-checked'?: boolean;

610

'aria-disabled'?: boolean;

611

'aria-hidden'?: boolean;

612

'aria-pressed'?: boolean;

613

'aria-current'?: boolean | 'page' | 'step' | 'location' | 'date' | 'time';

614

615

// HTML attributes

616

id?: string;

617

className?: string;

618

role?: string;

619

tabIndex?: number;

620

style?: React.CSSProperties;

621

}

622

623

// Common prop patterns

624

interface BaseProps {

625

/** Whether the component is disabled */

626

isDisabled?: boolean;

627

/** CSS class name */

628

className?: string;

629

/** Inline styles */

630

style?: React.CSSProperties;

631

/** Data test ID for testing */

632

'data-testid'?: string;

633

}

634

635

interface FocusableProps extends BaseProps {

636

/** Auto-focus behavior */

637

autoFocus?: boolean;

638

/** Exclude from tab order */

639

excludeFromTabOrder?: boolean;

640

/** Focus event handlers */

641

onFocus?: (e: FocusEvent) => void;

642

onBlur?: (e: FocusEvent) => void;

643

}

644

645

interface PressableProps extends FocusableProps {

646

/** Press event handlers */

647

onPress?: (e: PressEvent) => void;

648

onPressStart?: (e: PressEvent) => void;

649

onPressEnd?: (e: PressEvent) => void;

650

onPressChange?: (isPressed: boolean) => void;

651

onPressUp?: (e: PressEvent) => void;

652

}

653

```