Switch
An on/off setting that takes effect the moment it flips.
spredo-crypto-screener
LiveEvery push to main is built and deployed
A preview address for every pull request
Visitors see a maintenance page
- Click a switch or its title; Space or Enter flips the one in focus.
- Turn on Maintenance mode: the status above changes at once, with nothing to save.
- Deploy on push is disabled meanwhile: it shows off and Tab passes it by.
Field marks
- Import
import { Switch } from "cennge-ui";- Size
- 1.2 KB gzip for this import, its dependencies aside
- Pattern
- WAI-ARIA switch (Radix Switch): a
<button role="switch"> - Keyboard
- SpaceEnter
- State
- Controlled with
isSelectedandonValueChange, or uncontrolled withdefaultSelected - SSR
- Rendered on the server in the Next.js check; hydrates without a warning
- Forms
- Submits
"on"undernamewhile on - Tests
- Unit tests
- Source
- src/components/switch
When to use#
A Switch is a setting that applies as soon as it changes: notifications, preview deployments, a maintenance page. If the change waits for a Save button, use a Checkbox instead, so nobody expects the flip alone to do it.
Similar#
- Checkbox is a choice sent with a form, and can be mixed for a list.
- ToggleGroup switches between named options, such as Code and Preview, rather than on and off.
A labelled switch#
children become a <label> next to the track, so a click on the text flips it too. onValueChange gets the new state as a boolean.
Reviews and merges in cennge-ui email dev@example.com.
import { useState } from "react";
import { Switch } from "cennge-ui";
export default function NotificationsSwitch() {
const [isOn, setOn] = useState(true);
return (
<div className="flex flex-col gap-2">
<Switch isSelected={isOn} onValueChange={setOn}>
Pull request notifications
</Switch>
<p className="text-sm text-muted">
{isOn ? "Reviews and merges in cennge-ui email dev@example.com." : "No email for pull requests."}
</p>
</div>
);
}With a description#
For a settings list, put the title and its description beside the switch yourself: a <label htmlFor> for the title, and aria-describedby on the switch for the description. Every other prop passes to the switch's button, so id and aria-* land where screen readers look.
Pull requests merge by themselves once every check passes
import { useId } from "react";
import { Switch } from "cennge-ui";
export default function AutoMergeSetting() {
const id = useId();
return (
<div className="flex w-full max-w-md items-start justify-between gap-6">
<div className="flex flex-col gap-0.5">
<label htmlFor={id} className="cursor-pointer text-sm font-medium">
Allow auto-merge
</label>
<p id={`${id}-description`} className="text-xs text-muted">
Pull requests merge by themselves once every check passes
</p>
</div>
<Switch id={id} aria-describedby={`${id}-description`} defaultSelected />
</div>
);
}Colours and sizes#
color paints the track when it is on; primary follows the accent. A disabled switch keeps its state, fades, and leaves the tab order.
import { Switch, type CenngeColor } from "cennge-ui";
const colors: CenngeColor[] = ["default", "primary", "secondary", "success", "warning", "danger", "info"];
export default function SwitchColors() {
return (
<div className="flex flex-col gap-5">
<div className="flex flex-wrap gap-x-6 gap-y-3">
{colors.map((color) => (
<Switch key={color} color={color} defaultSelected>
{color[0]!.toUpperCase() + color.slice(1)}
</Switch>
))}
</div>
<div className="flex flex-wrap items-center gap-x-6 gap-y-3">
<Switch size="sm" defaultSelected>
Small
</Switch>
<Switch size="md" defaultSelected>
Medium
</Switch>
<Switch size="lg" defaultSelected>
Large
</Switch>
<Switch isDisabled defaultSelected>
Disabled
</Switch>
</div>
</div>
);
}Accessibility#
The switch is a <button role="switch"> with aria-checked, so screen readers announce it as a switch that is on or off. Without visible text, give it an aria-label. The thumb's movement follows the reduced-motion setting: with it, the thumb jumps rather than slides.
Props#
Also takes every attribute of <button>, except color, onChange, value, defaultValue, which mean something else here.