Switch
An on/off control with a thumb that moves along a track.
On this page
Code
import { Label } from "@bosunkit/components/label";
import { SaveChip, type SaveState } from "@bosunkit/components/setting-control";
import { Switch } from "@bosunkit/components/switch";
import { Anchor, BellRing, Moon, Send, Waves, type LucideIcon } from "lucide-react";
import {
useEffect,
useId,
useRef,
useState,
type CSSProperties,
type Dispatch,
type ReactElement,
type ReactNode,
type RefObject,
type SetStateAction,
} from "react";
export const description: string =
"The alarms at anchor: a switch that saves as it flips, one that is disabled while the anchor alarm is off, " +
"one held on by the skipper, and ones that start on and off, each named by its label.";
const SAVE_MILLISECONDS: number = 600;
const SAVED_MILLISECONDS: number = 1800;
const ROW: CSSProperties = {
display: "grid",
gridTemplateColumns: "18px minmax(0, 1fr) auto",
alignItems: "center",
gap: "4px var(--space-md)",
padding: "10px 0",
borderBottom: "1px solid var(--border)",
};
const NOTE: CSSProperties = { gridColumn: 2, fontSize: 12, color: "var(--text-dim)" };
function Setting({
icon: Icon,
id,
name,
note,
noteId,
children,
}: {
icon: LucideIcon;
id: string;
name: string;
note: string;
noteId?: string | undefined;
children: ReactNode;
}): ReactElement {
return (
<div style={ROW}>
<Icon size={16} aria-hidden="true" style={{ color: "var(--text-dim)" }} />
<Label htmlFor={id} style={{ marginBottom: 0, cursor: "pointer" }}>
{name}
</Label>
<div style={{ display: "flex", alignItems: "center", gap: "var(--space-sm)", gridRow: "span 2" }}>{children}</div>
<span id={noteId} style={NOTE}>
{note}
</span>
</div>
);
}
export default function Showcase(): ReactElement {
const id: string = useId();
const [anchorAlarm, setAnchorAlarm]: [boolean, Dispatch<SetStateAction<boolean>>] = useState<boolean>(true);
const [atNight, setAtNight]: [boolean, Dispatch<SetStateAction<boolean>>] = useState<boolean>(true);
const [save, setSave]: [SaveState, Dispatch<SetStateAction<SaveState>>] = useState<SaveState>("idle");
const timer: RefObject<number | undefined> = useRef<number | undefined>(undefined);
useEffect((): (() => void) => {
return (): void => {
window.clearTimeout(timer.current);
};
}, []);
function flipAnchorAlarm(checked: boolean): void {
setAnchorAlarm(checked);
setSave("saving");
window.clearTimeout(timer.current);
timer.current = window.setTimeout((): void => {
setSave("saved");
timer.current = window.setTimeout((): void => {
setSave("idle");
}, SAVED_MILLISECONDS);
}, SAVE_MILLISECONDS);
}
return (
<div style={{ width: "100%", maxWidth: 560 }}>
<Setting
icon={Anchor}
id={`${id}-anchor`}
name="Anchor alarm"
note={anchorAlarm ? "Sounds when SV Althea leaves the 45 m swing radius." : "The anchor is not watched."}
>
<SaveChip state={save} />
<Switch id={`${id}-anchor`} checked={anchorAlarm} onCheckedChange={flipAnchorAlarm} />
</Setting>
<Setting
icon={Moon}
id={`${id}-night`}
name="Sound at night"
noteId={`${id}-night-note`}
note={
anchorAlarm
? "Plays through the cabin speakers from 22:00 to 06:00, not only in the cockpit."
: "Turn the anchor alarm on to choose where it sounds at night."
}
>
<Switch
id={`${id}-night`}
checked={anchorAlarm && atNight}
disabled={!anchorAlarm}
aria-describedby={`${id}-night-note`}
onCheckedChange={setAtNight}
/>
</Setting>
<Setting
icon={BellRing}
id={`${id}-skipper`}
name="Wake the skipper too"
note="Anneke hears every alarm in the aft cabin, not only the watch leader."
>
<Switch id={`${id}-skipper`} defaultChecked />
</Setting>
<Setting
icon={Send}
id={`${id}-telegram`}
name="Send alarms to Telegram"
note="Off: there is no mobile signal at this anchorage, so messages would wait until the next harbor."
>
<Switch id={`${id}-telegram`} name="telegram" value="on" />
</Setting>
<Setting
icon={Waves}
id={`${id}-bilge`}
name="Bilge water high"
noteId={`${id}-bilge-note`}
note="Always on at sea; the skipper set it so. Ask Anneke to change it in harbor."
>
<Switch id={`${id}-bilge`} checked disabled aria-describedby={`${id}-bilge-note`} />
</Setting>
</div>
);
}
A switch is an on/off control for a setting that takes effect when it is flipped. Its state is data-state
(checked or unchecked) and aria-checked. It has no text of its own, so give it an accessible name with
aria-label or a label that points at it. It takes the props of Radix's switch.
Examples
Off
Code
import { Switch } from "@bosunkit/components/switch";
import type { ReactElement } from "react";
export const description: string = "Off, the thumb rests at the start of the track. A tap turns it on.";
export default function Off(): ReactElement {
return <Switch aria-label="Anchor alarm" />;
}
On
Code
import { Switch } from "@bosunkit/components/switch";
import type { ReactElement } from "react";
export const description: string = "On, with defaultChecked, the thumb sits at the end of the track. A tap turns it off.";
export default function On(): ReactElement {
return <Switch aria-label="Anchor alarm" defaultChecked />;
}
Disabled
Code
import { Switch } from "@bosunkit/components/switch";
import type { ReactElement } from "react";
export const description: string = "Disabled, the switch is dimmed and takes no tap.";
export default function Disabled(): ReactElement {
return <Switch aria-label="Anchor alarm" disabled />;
}
A stable name and a confirmed state
Name the setting, such as “Automatic updates,” and keep that label stable as the state changes. checked
expresses the value; onCheckedChange requests the next one. An uncontrolled switch with defaultChecked
only keeps local UI state and does not save a vessel setting.
For an asynchronous setting, use SettingToggle or show pending and failure feedback beside the switch. Keep the confirmed state authoritative and explain an unavailable choice without relying on a hover hint. A switch has two states; use a select or segmented control for Auto plus explicit modes. Test Space from keyboard focus and the entire labeled target on touch.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | ((state: State) => string | undefined) | undefined | – | CSS class applied to the element, or a function that returns a class based on the component's state. |
| render | React.ReactElement | ComponentRenderFn<RenderFunctionProps, State> | undefined | – | Allows you to replace the component's HTML element
with a different tag, or compose it with another component.
Accepts a ReactElement or a function that returns the element to render. |
| style | React.CSSProperties | ((state: State) => React.CSSProperties | undefined) | undefined | – | Style applied to the element, or a function that returns a style object based on the component's state. |
| nativeButton | boolean | undefined | – | Whether the component renders a native <button> element when replacing it
via the render prop.
Set to true if the rendered element is a native button. |
| id | string | undefined | – | The id of the hidden input element.
When nativeButton is true, the id is applied to the root element. |
| checked | boolean | undefined | – | Whether the switch is currently active.
To render an uncontrolled switch, use the defaultChecked prop instead. |
| defaultChecked | boolean | undefined | – | Whether the switch is initially active.
To render a controlled switch, use the checked prop instead. |
| disabled | boolean | undefined | – | Whether the component should ignore user interaction. |
| inputRef | React.Ref<HTMLInputElement> | undefined | – | A ref to access the hidden <input> element. |
| name | string | undefined | – | Identifies the field when a form is submitted. |
| form | string | undefined | – | Identifies the form that owns the hidden input. Useful when the switch is rendered outside the form. |
| onCheckedChange | ((checked: boolean, eventDetails: SwitchRoot.ChangeEventDetails) => void) | undefined | – | Event handler called when the switch is activated or deactivated. |
| readOnly | boolean | undefined | – | Whether the user should be unable to activate or deactivate the switch. |
| required | boolean | undefined | – | Whether the user must activate the switch before submitting a form. |
| value | string | undefined | – | The value submitted with the form when the switch is on. By default, switch submits the "on" value, matching native checkbox behavior. |
| uncheckedValue | string | undefined | – | The value submitted with the form when the switch is off. By default, unchecked switches do not submit any value, matching native checkbox behavior. |
Also takes every attribute of Primitive.Root.Props.
Related
-
Checkbox: independent choices collected as part of a form.
-
Label: the caption that names a switch.
-
Accessibility: the target sizes and the accessible name a control needs.