A two-state control used to toggle between checked and unchecked states
npx @tessl/cli install tessl/npm-radix-ui--react-switch@1.2.00
# Radix UI React Switch
1
2
@radix-ui/react-switch is a TypeScript React component library that provides an accessible, unstyled switch component. It implements a two-state control for toggling between checked and unchecked states with proper ARIA attributes, keyboard navigation, and form integration.
3
4
## Package Information
5
6
- **Package Name**: @radix-ui/react-switch
7
- **Package Type**: npm
8
- **Language**: TypeScript
9
- **Installation**: `npm install @radix-ui/react-switch`
10
11
## Core Imports
12
13
```typescript
14
import * as Switch from "@radix-ui/react-switch";
15
```
16
17
Or with named imports:
18
19
```typescript
20
import { Switch, SwitchThumb, Root, Thumb } from "@radix-ui/react-switch";
21
```
22
23
For CommonJS:
24
25
```javascript
26
const Switch = require("@radix-ui/react-switch");
27
```
28
29
## Basic Usage
30
31
```typescript
32
import * as Switch from "@radix-ui/react-switch";
33
import { useState } from "react";
34
35
function SwitchDemo() {
36
const [checked, setChecked] = useState(false);
37
38
return (
39
<form>
40
<div style={{ display: "flex", alignItems: "center" }}>
41
<label
42
className="Label"
43
htmlFor="airplane-mode"
44
style={{ paddingRight: 15 }}
45
>
46
Airplane mode
47
</label>
48
<Switch.Root
49
className="SwitchRoot"
50
id="airplane-mode"
51
checked={checked}
52
onCheckedChange={setChecked}
53
>
54
<Switch.Thumb className="SwitchThumb" />
55
</Switch.Root>
56
</div>
57
</form>
58
);
59
}
60
```
61
62
## Architecture
63
64
The component follows Radix UI's compound component pattern with two main parts:
65
66
- **Switch/Root**: The main interactive button element that handles state and accessibility
67
- **SwitchThumb/Thumb**: The visual indicator that shows the current state
68
69
The component supports both controlled and uncontrolled usage patterns, integrates seamlessly with HTML forms, and provides proper ARIA attributes for screen readers.
70
71
## Capabilities
72
73
### Switch Component
74
75
The main switch component that renders as an accessible button with switch role.
76
77
```typescript { .api }
78
/**
79
* Base primitive components from @radix-ui/react-primitive
80
*/
81
type PrimitivePropsWithRef<E extends React.ElementType> = React.ComponentPropsWithRef<E> & {
82
asChild?: boolean;
83
};
84
85
interface PrimitiveForwardRefComponent<E extends React.ElementType>
86
extends React.ForwardRefExoticComponent<PrimitivePropsWithRef<E>> {}
87
88
declare const Primitive: {
89
button: PrimitiveForwardRefComponent<"button">;
90
span: PrimitiveForwardRefComponent<"span">;
91
input: PrimitiveForwardRefComponent<"input">;
92
};
93
94
/**
95
* Element type for Switch component
96
*/
97
type SwitchElement = React.ComponentRef<typeof Primitive.button>;
98
99
/**
100
* Props type for Primitive.button
101
*/
102
type PrimitiveButtonProps = React.ComponentPropsWithoutRef<typeof Primitive.button>;
103
104
/**
105
* Main switch component that renders as a button with switch role
106
*/
107
const Switch: React.ForwardRefExoticComponent<
108
SwitchProps & React.RefAttributes<SwitchElement>
109
>;
110
111
/**
112
* Alternative export name for the Switch component
113
*/
114
const Root: React.ForwardRefExoticComponent<
115
SwitchProps & React.RefAttributes<SwitchElement>
116
>;
117
118
interface SwitchProps extends PrimitiveButtonProps {
119
/** The controlled checked state of the switch */
120
checked?: boolean;
121
/** The checked state of the switch when it is initially rendered. Use when you do not need to control its checked state */
122
defaultChecked?: boolean;
123
/** When true, indicates that the user must check the switch before the owning form can be submitted */
124
required?: boolean;
125
/** The name of the switch. Submitted with its owning form as part of a name/value pair */
126
name?: string;
127
/** The value given as data when submitted with a `name`. Defaults to "on" */
128
value?: string;
129
/** Associates the control with a form element */
130
form?: string;
131
/** When true, prevents the user from interacting with the switch */
132
disabled?: boolean;
133
/** Event handler called when the checked state of the switch changes */
134
onCheckedChange?(checked: boolean): void;
135
}
136
```
137
138
**Key Features:**
139
- Supports both controlled (`checked` + `onCheckedChange`) and uncontrolled (`defaultChecked`) patterns
140
- Automatically integrates with HTML forms through hidden input element with `name` and `value` props
141
- Provides proper ARIA attributes (`role="switch"`, `aria-checked`, `aria-required`)
142
- Handles keyboard navigation (Space and Enter keys toggle state)
143
- Includes form integration with proper event bubbling and validation support
144
- Uses internal `SwitchBubbleInput` component for seamless form submission
145
146
**Data Attributes:**
147
- `data-state`: "checked" | "unchecked" - Current switch state
148
- `data-disabled`: Present when disabled
149
150
### Switch Thumb Component
151
152
The visual indicator component that displays the current state of the switch.
153
154
```typescript { .api }
155
/**
156
* Element type for SwitchThumb component
157
*/
158
type SwitchThumbElement = React.ComponentRef<typeof Primitive.span>;
159
160
/**
161
* Props type for Primitive.span
162
*/
163
type PrimitiveSpanProps = React.ComponentPropsWithoutRef<typeof Primitive.span>;
164
165
/**
166
* Visual thumb/handle component that indicates the switch state
167
*/
168
const SwitchThumb: React.ForwardRefExoticComponent<
169
SwitchThumbProps & React.RefAttributes<SwitchThumbElement>
170
>;
171
172
/**
173
* Alternative export name for the SwitchThumb component
174
*/
175
const Thumb: React.ForwardRefExoticComponent<
176
SwitchThumbProps & React.RefAttributes<SwitchThumbElement>
177
>;
178
179
interface SwitchThumbProps extends PrimitiveSpanProps {}
180
```
181
182
**Key Features:**
183
- Automatically receives state from parent Switch component through context
184
- Renders as a span element for styling flexibility
185
- Inherits data attributes from Switch context for consistent styling
186
187
**Data Attributes:**
188
- `data-state`: "checked" | "unchecked" - Current switch state
189
- `data-disabled`: Present when disabled
190
191
### Context Utilities
192
193
Utility function for creating component scopes when composing multiple switch instances.
194
195
```typescript { .api }
196
/**
197
* Scope type for context management
198
*/
199
type Scope<C = any> = { [scopeName: string]: React.Context<C>[] } | undefined;
200
201
/**
202
* Hook type returned by createSwitchScope
203
*/
204
type ScopeHook = (scope: Scope) => { [__scopeSwitch: string]: Scope };
205
206
/**
207
* Creates a scope for switch components to avoid context conflicts
208
* Used when composing multiple switch instances or building compound components
209
*/
210
function createSwitchScope(): ScopeHook;
211
```
212
213
**Usage:**
214
```typescript
215
import { createSwitchScope } from "@radix-ui/react-switch";
216
217
// Create a scoped context for this specific switch instance
218
const useScope = createSwitchScope();
219
220
function MyComponent() {
221
const scope = useScope();
222
223
return (
224
<Switch.Root {...scope}>
225
<Switch.Thumb />
226
</Switch.Root>
227
);
228
}
229
```
230
231
## Usage Examples
232
233
### Controlled Switch
234
235
```typescript
236
import * as Switch from "@radix-ui/react-switch";
237
import { useState } from "react";
238
239
function ControlledSwitch() {
240
const [isChecked, setIsChecked] = useState(false);
241
242
return (
243
<Switch.Root checked={isChecked} onCheckedChange={setIsChecked}>
244
<Switch.Thumb />
245
</Switch.Root>
246
);
247
}
248
```
249
250
### Uncontrolled Switch
251
252
```typescript
253
import * as Switch from "@radix-ui/react-switch";
254
255
function UncontrolledSwitch() {
256
return (
257
<Switch.Root defaultChecked={true}>
258
<Switch.Thumb />
259
</Switch.Root>
260
);
261
}
262
```
263
264
### Form Integration
265
266
```typescript
267
import * as Switch from "@radix-ui/react-switch";
268
269
function FormSwitch() {
270
return (
271
<form onSubmit={(e) => {
272
e.preventDefault();
273
const formData = new FormData(e.currentTarget);
274
console.log(formData.get("notifications")); // "on" or null
275
}}>
276
<div style={{ display: "flex", alignItems: "center" }}>
277
<label htmlFor="notifications-switch" style={{ paddingRight: 15 }}>
278
Enable notifications
279
</label>
280
<Switch.Root
281
id="notifications-switch"
282
name="notifications"
283
value="on"
284
defaultChecked={false}
285
>
286
<Switch.Thumb />
287
</Switch.Root>
288
</div>
289
<button type="submit">Submit</button>
290
</form>
291
);
292
}
293
```
294
295
**Form Integration Details:**
296
- The `name` prop determines the form field name in the submitted data
297
- The `value` prop sets the value when checked (defaults to "on")
298
- When unchecked, the field is not included in form submission (standard HTML behavior)
299
- A hidden input element is automatically created to handle form submission
300
301
### Disabled Switch
302
303
```typescript
304
import * as Switch from "@radix-ui/react-switch";
305
306
function DisabledSwitch() {
307
return (
308
<div style={{ display: "flex", alignItems: "center" }}>
309
<label htmlFor="disabled-switch" style={{ paddingRight: 15 }}>
310
Cannot change this
311
</label>
312
<Switch.Root id="disabled-switch" disabled defaultChecked={true}>
313
<Switch.Thumb />
314
</Switch.Root>
315
</div>
316
);
317
}
318
```
319
320
### With Custom Styling
321
322
```typescript
323
import * as Switch from "@radix-ui/react-switch";
324
import "./switch.css"; // Your custom styles
325
326
function StyledSwitch() {
327
return (
328
<Switch.Root className="switch-root">
329
<Switch.Thumb className="switch-thumb" />
330
</Switch.Root>
331
);
332
}
333
```
334
335
Example CSS:
336
337
```css
338
.switch-root {
339
width: 42px;
340
height: 25px;
341
background-color: gray;
342
border-radius: 9999px;
343
position: relative;
344
border: none;
345
cursor: pointer;
346
}
347
348
.switch-root[data-state="checked"] {
349
background-color: blue;
350
}
351
352
.switch-root[data-disabled] {
353
opacity: 0.5;
354
cursor: not-allowed;
355
}
356
357
.switch-thumb {
358
display: block;
359
width: 21px;
360
height: 21px;
361
background-color: white;
362
border-radius: 9999px;
363
transition: transform 100ms;
364
transform: translateX(2px);
365
will-change: transform;
366
}
367
368
.switch-thumb[data-state="checked"] {
369
transform: translateX(19px);
370
}
371
372
.switch-thumb[data-disabled] {
373
opacity: 0.8;
374
}
375
```
376
377
## Accessibility Features
378
379
- **ARIA Compliant**: Uses proper `role="switch"`, `aria-checked`, and `aria-required` attributes
380
- **Keyboard Navigation**: Space and Enter keys toggle the switch state
381
- **Screen Reader Support**: State changes are announced to assistive technologies
382
- **Focus Management**: Proper focus indicators and keyboard navigation
383
- **Form Integration**: Works seamlessly with native HTML form validation and submission
384
- **Disabled State**: Properly handles disabled state with appropriate ARIA attributes and visual indicators