Switch
Free · MITReact Native · ExpoAn iOS-style switch with squash and stretch: the thumb stretches toward where it is going on touch down, then travels on a spring while the track color follows. Settings-row layout, tones, sizes and optimistic async changes.
Installation
$ npx shadcn@latest add https://goodui.dev/native/r/switch.jsonThen run npx expo install --fix so react-native-reanimated match your Expo SDK. Files land in components/goodui-native/switch.
Usage
import { useState } from "react";
import { Switch } from "@/components/goodui-native/switch/switch";
export function PushSetting() {
const [push, setPush] = useState(true);
return (
<Switch
label="Push notifications"
description="Bookings, reminders and messages"
value={push}
onValueChange={async (next) => {
await api.saveSettings({ push: next });
setPush(next);
}}
/>
);
}When to use
- On or off settings that take effect immediately: notifications, dark mode, Face ID.
- Settings saved to a server: return the promise from onValueChange.
- A settings list: pass label and description for the full row.
- Choices submitted later with a form, or several options in a list: use checkbox.
- One of two named views: use segmented-control.
- A destructive change: use hold-to-confirm.
Examples
Inline with a screen reader label
<Switch accessibilityLabel="Round-up savings" defaultValue tone="success" />Small
<Switch size="sm" accessibilityLabel="Show weekends" value={weekends} onValueChange={setWeekends} />API reference
| Prop | Type | Default |
|---|---|---|
| value On or off (controlled). Pair it with `onValueChange`. | boolean | — |
| defaultValue Initial state when uncontrolled. | boolean | false |
| onValueChange Called with the new state. Return a promise (for example a settings request) and the switch flips right away, shows a spinner in the thumb while pending, and flips back with an error haptic and a shake if it rejects. | (value: boolean) => unknown | — |
| tone Track color when on. `accent` by default; `success`, `warning` and `danger` only when the setting means that status. | "success" | "warning" | "danger" | "accent" | "accent" |
| size 40 × 24 (`sm`) or 51 × 31 pt (`md`, the iOS size). The touch target stays 44 pt. | "sm" | "md" | "md" |
| label Turns it into a settings row: the label on the leading side, the switch trailing, the whole row toggles. | string | — |
| description Secondary line under the row label. | string | — |
| disabled Dims the control and ignores presses. | boolean | false |
| haptic `false` silences the select haptic on change. | boolean | true |
| accessibilityLabel Screen reader label when there is no visible `label`. | string | — |
| accessibilityHint Describes what happens on activation, read after the label. | string | — |
| labels Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide. | Partial<SwitchLabels> | — |
| style Style for the outer container. | StyleProp<ViewStyle> | — |
| testID Identifier for end-to-end tests. | string | — |
Motion and haptics
- Press: the thumb widens toward the far side and squashes to 92 % height on the snappy spring, from touch down.
- Change: the thumb travels on the snappy spring and the track color interpolates with it.
- Pending: a spinner turns inside the thumb. Rejected: the switch flips back and shakes.
- Reduced motion: no squash, stretch or shake; the thumb moves with a short timing.
- Haptic: toggle-on or toggle-off on change
- Haptic: error when a returned promise rejects
- Thumb geometry and colors animate on the UI thread; a toggle is one React update.
Accessibility
- accessibilityRole is switch with checked, disabled and busy (while a returned promise is pending) in accessibilityState.
- In the row layout the whole row is the switch, labelled 'label, description'. Inline, pass accessibilityLabel.
- A rejected change is announced ('Couldn't change the setting').
- The inline switch has hitSlop up to a 44 pt target; the row is at least 44 pt tall.
Notes for AI
- Controlled (value + onValueChange) or uncontrolled (defaultValue).
- Return a promise from onValueChange to get optimistic flipping, a pending spinner and automatic revert on failure. Do not add your own loading state.
- Use label and description instead of building your own row around it.
- Use tone only when the setting means a status; the default accent is right for almost everything.
Related
Checkboxes whose check draws itself in (an indeterminate dash retracts into it) and radios whose dot pops into the center, inline or as list rows, with CheckboxGroup and RadioGroup helpers.
Two to five views of the same thing with an elastic highlight that stretches toward the new segment, can be dragged, and ticks at every boundary. Includes a billing toggle.
Confirms an irreversible action by holding: ink fills from the leading edge, the label inverts where the ink passes, and haptics tick at every quarter.