Switch
A track and thumb that turns one setting on or off. Use it for settings that take effect at once, such as notifications or model access.
Sizes and states
default is a 32 × 18px track for settings rows. sm is a 24 × 14px track for dense control strips.
Off
On
Disabled
Disabled on
Invalid
Controlled with a label
Wrap the switch in a Label and pass a state setter to onCheckedChange. Clicking the text toggles it.
Bud is idling.
In settings rows
Give the switch an id and pass the same id to SettingsRow htmlFor, so the row label names and toggles it.
Local preferences
Project name
Model access
Each row names its switch with aria-label. While a save runs, the row says Saving… and its switch is aria-busy. Every switch is disabled while a save, reload, or conflict is unresolved.
- Claude Sonnet 4.5Credits per 1M input / output tokens: 3 / 15
- GPT-5Credits per 1M input / output tokens: 1.25 / 10
- DeepSeek V3.2Credits per 1M input / output tokens: 0.28 / 0.42
- Kimi K2 Thinking with extended reasoningCredits per 1M input / output tokens: 0.6 / 2.5
- Grok 4Credits per 1M input / output tokens: Unknown / Unknown
Usage
import { Switch } from "@tulipfarm/sprout/switch";
import { Label } from "@tulipfarm/sprout/label";
import { SettingsRow } from "@tulipfarm/sprout/settings-list";
const [paused, setPaused] = useState(false);
<Label>
<Switch checked={paused} onCheckedChange={setPaused} />
Pause motion
</Label>
<SettingsRow label="Email summaries" htmlFor="summary">
<Switch id="summary" defaultChecked />
</SettingsRow>
<Switch
aria-label={`Allow ${model.name}`}
checked={model.enabled}
disabled={saving}
aria-busy={pending}
onCheckedChange={(enabled) => save(model.id, enabled)}
/>Pass checked and onCheckedChange to control the switch, or defaultChecked to let it keep its own state.onCheckedChange gets a boolean, so a state setter can be passed straight in.
Props
size is "default" (a 32 × 18px track with a 16px thumb) or "sm" (a 24 × 14px track with a 12px thumb). Both have a hit area 12px wider and 8px taller on each side.
disabled dims the switch and blocks changes; a disabled<fieldset> around it does the same. Setaria-invalid to draw a danger border and ring. Setaria-busy while a change is saving to show a progress cursor.
The switch also takes Base UI props such as id,name, value, required,readOnly, inputRef, and state functions forclassName.
Accessibility
Switch renders an element with role="switch" andaria-checked, plus a hidden input for forms. Tab moves focus to it, and Space or Enter toggles it. A focused switch shows a 2px focus ring. Name every switch with a wrapping Label, aLabel htmlFor or SettingsRow htmlFor, oraria-label.
The track color changes in 120ms and the thumb slides in 180ms. Both stop when reduced motion is requested. The thumb slides the other way in right-to-left text. In forced colors mode the track gets a system border and an on switch is highlighted.
Theme tokens
Off uses a --sprout-muted track with a--sprout-surface thumb (--sprout-text in dark mode). On uses --sprout-primary with a--sprout-on-primary thumb, so the opt-in TulipFarm theme tints it green. Invalid uses --sprout-danger, and focus uses--sprout-focus.