or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

tessl/npm-react-aria--radio

React hooks for building accessible radio buttons and radio groups with WAI-ARIA compliance.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@react-aria/radio@3.12.x

To install, run

npx @tessl/cli install tessl/npm-react-aria--radio@3.12.0

0

# @react-aria/radio

1

2

@react-aria/radio provides React hooks for building accessible radio buttons and radio groups with full WAI-ARIA compliance. It implements the Radio Group pattern with complete keyboard navigation, screen reader support, and mouse/touch interactions while remaining completely unstyled to allow custom styling.

3

4

## Package Information

5

6

- **Package Name**: @react-aria/radio

7

- **Package Type**: npm

8

- **Language**: TypeScript

9

- **Installation**: `npm install @react-aria/radio`

10

11

## Core Imports

12

13

```typescript

14

import { useRadio, useRadioGroup } from "@react-aria/radio";

15

import type { AriaRadioProps, AriaRadioGroupProps, RadioAria, RadioGroupAria, Orientation } from "@react-aria/radio";

16

```

17

18

For CommonJS:

19

20

```javascript

21

const { useRadio, useRadioGroup } = require("@react-aria/radio");

22

```

23

24

## Basic Usage

25

26

```typescript

27

import { useRadio, useRadioGroup } from "@react-aria/radio";

28

import { useRadioGroupState } from "@react-stately/radio"; // Required peer dependency

29

import { useRef } from "react";

30

31

// Radio Group Component

32

function MyRadioGroup(props) {

33

const state = useRadioGroupState(props);

34

const { radioGroupProps, labelProps } = useRadioGroup(props, state);

35

36

return (

37

<div {...radioGroupProps}>

38

<span {...labelProps}>{props.label}</span>

39

{props.children}

40

</div>

41

);

42

}

43

44

// Individual Radio Component

45

function MyRadio(props) {

46

const ref = useRef();

47

const state = useRadioGroupState(props.groupProps);

48

const { inputProps, labelProps, isSelected, isDisabled } = useRadio(props, state, ref);

49

50

return (

51

<label {...labelProps}>

52

<input {...inputProps} ref={ref} />

53

{props.children}

54

</label>

55

);

56

}

57

58

// Usage

59

<MyRadioGroup label="Favorite pet" value={selectedPet} onChange={setSelectedPet}>

60

<MyRadio value="dogs">Dogs</MyRadio>

61

<MyRadio value="cats">Cats</MyRadio>

62

<MyRadio value="birds">Birds</MyRadio>

63

</MyRadioGroup>

64

```

65

66

## Architecture

67

68

@react-aria/radio is built around the separation of behavior and presentation:

69

70

- **Behavior Layer**: The hooks provide all accessibility, keyboard navigation, focus management, and interaction logic

71

- **State Management**: Integrates with @react-stately/radio for radio group state management

72

- **Unstyled Approach**: No CSS or styling is provided, allowing complete visual customization

73

- **ARIA Compliance**: Full implementation of WAI-ARIA Radio Group pattern

74

- **Form Integration**: Native HTML form support with validation

75

76

## Capabilities

77

78

### Radio Group Management

79

80

Provides behavior and accessibility implementation for radio group components that allow users to select a single item from mutually exclusive options.

81

82

```typescript { .api }

83

/**

84

* Provides the behavior and accessibility implementation for a radio group component.

85

* @param props - Props for the radio group

86

* @param state - State for the radio group, as returned by useRadioGroupState

87

* @returns RadioGroupAria object with props and validation state

88

*/

89

function useRadioGroup(props: AriaRadioGroupProps, state: RadioGroupState): RadioGroupAria;

90

91

interface AriaRadioGroupProps extends RadioGroupProps, InputDOMProps, DOMProps, AriaLabelingProps, AriaValidationProps {

92

/** The axis the radio buttons should align with */

93

orientation?: Orientation;

94

/** The name of the radio group for form submission */

95

name?: string;

96

/** Whether the radio group is disabled */

97

isDisabled?: boolean;

98

/** Whether the radio group is read-only */

99

isReadOnly?: boolean;

100

/** Whether the radio group is required */

101

isRequired?: boolean;

102

/** Validation behavior for the radio group */

103

validationBehavior?: 'aria' | 'native';

104

/** Handler called when the radio group receives focus */

105

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

106

/** Handler called when the radio group loses focus */

107

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

108

/** Handler called when the focus changes within the radio group */

109

onFocusChange?: (isFocused: boolean) => void;

110

}

111

112

interface RadioGroupAria extends ValidationResult {

113

/** Props for the radio group wrapper element */

114

radioGroupProps: DOMAttributes;

115

/** Props for the radio group's visible label (if any) */

116

labelProps: DOMAttributes;

117

/** Props for the radio group description element, if any */

118

descriptionProps: DOMAttributes;

119

/** Props for the radio group error message element, if any */

120

errorMessageProps: DOMAttributes;

121

}

122

```

123

124

**Usage Example:**

125

126

```typescript

127

import { useRadioGroup } from "@react-aria/radio";

128

import { useRadioGroupState } from "@react-stately/radio";

129

130

function RadioGroup({ label, children, ...props }) {

131

const state = useRadioGroupState(props);

132

const {

133

radioGroupProps,

134

labelProps,

135

descriptionProps,

136

errorMessageProps,

137

isInvalid,

138

validationErrors

139

} = useRadioGroup(props, state);

140

141

return (

142

<div {...radioGroupProps}>

143

<span {...labelProps}>{label}</span>

144

{props.description && <div {...descriptionProps}>{props.description}</div>}

145

{children}

146

{isInvalid && <div {...errorMessageProps}>{validationErrors.join(' ')}</div>}

147

</div>

148

);

149

}

150

```

151

152

### Individual Radio Button Management

153

154

Provides behavior and accessibility implementation for individual radio buttons within a radio group.

155

156

```typescript { .api }

157

/**

158

* Provides the behavior and accessibility implementation for an individual radio button.

159

* @param props - Props for the radio

160

* @param state - State for the radio group, as returned by useRadioGroupState

161

* @param ref - Ref to the HTML input element

162

* @returns RadioAria object with props and state

163

*/

164

function useRadio(props: AriaRadioProps, state: RadioGroupState, ref: RefObject<HTMLInputElement | null>): RadioAria;

165

166

interface AriaRadioProps extends RadioProps, DOMProps, AriaLabelingProps, PressEvents {

167

/** The value of the radio button, used when submitting an HTML form */

168

value: string;

169

/** The label for the radio. Accepts any renderable node */

170

children?: ReactNode;

171

/** Whether the radio button is disabled */

172

isDisabled?: boolean;

173

/** Handler called when the radio is pressed */

174

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

175

/** Handler called when a press interaction starts */

176

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

177

/** Handler called when a press interaction ends */

178

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

179

/** Handler called when the press state changes */

180

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

181

/** Handler called when a press is released over the target */

182

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

183

/** Handler called when the element is clicked */

184

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

185

}

186

187

interface RadioAria {

188

/** Props for the label wrapper element */

189

labelProps: LabelHTMLAttributes<HTMLLabelElement>;

190

/** Props for the input element */

191

inputProps: InputHTMLAttributes<HTMLInputElement>;

192

/** Whether the radio is disabled */

193

isDisabled: boolean;

194

/** Whether the radio is currently selected */

195

isSelected: boolean;

196

/** Whether the radio is in a pressed state */

197

isPressed: boolean;

198

}

199

```

200

201

**Usage Example:**

202

203

```typescript

204

import { useRadio } from "@react-aria/radio";

205

import { useRef } from "react";

206

207

function Radio({ children, ...props }) {

208

const ref = useRef();

209

const { inputProps, labelProps, isSelected, isDisabled, isPressed } = useRadio(props, state, ref);

210

211

return (

212

<label

213

{...labelProps}

214

className={`radio ${isSelected ? 'selected' : ''} ${isDisabled ? 'disabled' : ''}`}

215

>

216

<input {...inputProps} ref={ref} />

217

<span className="radio-indicator" />

218

{children}

219

</label>

220

);

221

}

222

```

223

224

## Types

225

226

```typescript { .api }

227

/** Layout orientation for radio group */

228

type Orientation = 'horizontal' | 'vertical';

229

230

/** React node type */

231

type ReactNode = React.ReactNode;

232

233

/** Mouse event type */

234

type MouseEvent = React.MouseEvent;

235

236

/** State management for radio groups from @react-stately/radio */

237

interface RadioGroupState {

238

/** Currently selected value */

239

selectedValue: string | null;

240

/** Default selected value */

241

defaultSelectedValue: string | null;

242

/** Last focused value for keyboard navigation */

243

lastFocusedValue: string | null;

244

/** Whether the radio group is disabled */

245

isDisabled: boolean;

246

/** Whether the radio group is read-only */

247

isReadOnly: boolean;

248

/** Whether selection is required */

249

isRequired: boolean;

250

/** Whether the radio group is invalid */

251

isInvalid: boolean;

252

/** Current validation state */

253

displayValidation: ValidationResult;

254

/** Set the selected value */

255

setSelectedValue: (value: string) => void;

256

/** Set the last focused value */

257

setLastFocusedValue: (value: string | null) => void;

258

}

259

260

/** Base props for radio groups */

261

interface RadioGroupProps {

262

/** Currently selected value */

263

value?: string | null;

264

/** Default selected value for uncontrolled components */

265

defaultValue?: string | null;

266

/** Handler called when selection changes */

267

onChange?: (value: string) => void;

268

}

269

270

/** Base props for individual radio buttons */

271

interface RadioProps {

272

/** The value of the radio button */

273

value: string;

274

/** The label content */

275

children?: ReactNode;

276

/** Whether the radio is disabled */

277

isDisabled?: boolean;

278

}

279

280

/** DOM input properties */

281

interface InputDOMProps {

282

/** Form name attribute */

283

name?: string;

284

/** Form element */

285

form?: string;

286

}

287

288

/** Validation properties for ARIA */

289

interface AriaValidationProps {

290

/** Whether validation is required */

291

isRequired?: boolean;

292

/** Validation behavior */

293

validationBehavior?: 'aria' | 'native';

294

/** Custom validation function */

295

validate?: (value: string | null) => ValidationError | true | null | undefined;

296

/** Error message */

297

errorMessage?: string | ((validation: ValidationResult) => string);

298

}

299

300

/** ARIA labeling properties */

301

interface AriaLabelingProps {

302

/** Accessible label */

303

'aria-label'?: string;

304

/** ID of element that labels this element */

305

'aria-labelledby'?: string;

306

/** ID of element that describes this element */

307

'aria-describedby'?: string;

308

}

309

310

/** DOM properties */

311

interface DOMProps {

312

/** Element ID */

313

id?: string;

314

}

315

316

/** Press event handlers */

317

interface PressEvents {

318

/** Handler called when a press interaction starts */

319

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

320

/** Handler called when a press interaction ends */

321

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

322

/** Handler called when the press state changes */

323

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

324

/** Handler called when a press is released over the target */

325

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

326

/** Handler called when the element is pressed */

327

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

328

}

329

330

/** Validation result containing error information */

331

interface ValidationResult {

332

/** Whether the input is invalid */

333

isInvalid: boolean;

334

/** Array of validation error messages */

335

validationErrors: string[];

336

/** Detailed validation information */

337

validationDetails: ValidationDetails;

338

}

339

340

/** Detailed validation information */

341

interface ValidationDetails {

342

/** Validation errors from form constraints */

343

formErrors: string[];

344

/** Validation errors from custom validation */

345

validationErrors: string[];

346

}

347

348

/** Validation error */

349

interface ValidationError {

350

/** Error message */

351

message: string;

352

}

353

354

/** DOM attributes for elements */

355

interface DOMAttributes {

356

[key: string]: any;

357

}

358

359

/** Press event information */

360

interface PressEvent {

361

/** The type of press event */

362

type: 'pressstart' | 'pressend' | 'pressup' | 'press';

363

/** The pointer type that triggered the press event */

364

pointerType: 'mouse' | 'pen' | 'touch' | 'keyboard' | 'virtual';

365

/** The target element of the press event */

366

target: Element;

367

/** Whether the shift key was held during the press event */

368

shiftKey: boolean;

369

/** Whether the ctrl key was held during the press event */

370

ctrlKey: boolean;

371

/** Whether the meta key was held during the press event */

372

metaKey: boolean;

373

/** Whether the alt key was held during the press event */

374

altKey: boolean;

375

}

376

377

/** React ref object */

378

interface RefObject<T> {

379

readonly current: T | null;

380

}

381

382

/** HTML element attributes */

383

interface HTMLAttributes<T> {

384

[key: string]: any;

385

}

386

387

/** HTML label element attributes */

388

interface LabelHTMLAttributes<T> extends HTMLAttributes<T> {

389

htmlFor?: string;

390

}

391

392

/** HTML input element attributes */

393

interface InputHTMLAttributes<T> extends HTMLAttributes<T> {

394

type?: string;

395

name?: string;

396

value?: string | ReadonlyArray<string> | number;

397

checked?: boolean;

398

disabled?: boolean;

399

required?: boolean;

400

tabIndex?: number;

401

}

402

```

403

404

## Error Handling

405

406

The hooks handle validation and error states through the ValidationResult interface:

407

408

- **isInvalid**: Boolean indicating validation failure

409

- **validationErrors**: Array of error messages for display

410

- **validationDetails**: Detailed validation information

411

412

Common validation scenarios include:

413

- Required radio group with no selection

414

- Custom validation rules through the `validate` prop

415

- Form constraint validation

416

417

## Integration Requirements

418

419

@react-aria/radio is designed to work in conjunction with @react-stately/radio for state management. This separation allows for flexible state handling while the aria package focuses purely on accessibility and behavior.

420

421

**Installation:**

422

```bash

423

npm install @react-aria/radio @react-stately/radio

424

```

425

426

**Basic state setup:**

427

```typescript

428

import { useRadioGroupState, type RadioGroupState } from "@react-stately/radio";

429

430

const state = useRadioGroupState({

431

value: selectedValue,

432

onChange: setSelectedValue,

433

defaultValue: 'initial',

434

isDisabled: false,

435

isRequired: true

436

});

437

```

438

439

**Required peer dependencies:**

440

- `@react-stately/radio` - Provides RadioGroupState and useRadioGroupState hook

441

- `react` - React 16.8+ with hooks support (required for hook functionality)

442

- `react-dom` - DOM utilities and event handling

443

444

The package integrates seamlessly with HTML forms and supports both controlled and uncontrolled usage patterns. The aria hooks handle all accessibility concerns while state management is handled separately by the stately package.