Photon Design System

Switch

An on/off control for a setting that takes effect immediately — no submit required. Built on Base UI Switch, carrying the switch role and aria-checked. For a value confirmed later on a form, use Checkbox.

Import

import { Switch } from "@photon-ai/pho-ui/components/switch";

Sizes

Two sizes — sm and md (default). A switch is intentionally compact; there is no lg.

<Switch aria-label="Wi-Fi" size="sm" defaultChecked />
<Switch aria-label="Wi-Fi" defaultChecked />

States

Control with checked / onCheckedChange, or leave it uncontrolled with defaultChecked. disabled dims and blocks interaction.

<Switch aria-label="Notifications" defaultChecked />
<Switch aria-label="Notifications" />
<Switch aria-label="Notifications" defaultChecked disabled />

With a label

A lone switch is ambiguous. Give it a visible <label> and tie the two together with id / htmlFor — clicking the label then toggles the control. This is also the shape of a controlled switch, driving checked from state via onCheckedChange.

Join known networks automatically.

const [checked, setChecked] = useState(true);

<label htmlFor="wifi">Wi-Fi</label>
<Switch id="wifi" checked={checked} onCheckedChange={setChecked} />

Props

  • size — sm · md (default md)
  • checked — controlled on/off state
  • defaultChecked — uncontrolled initial state (default false)
  • onCheckedChange — (checked: boolean, event) => void, fired on toggle
  • disabled — non-interactive and dimmed (default false)
  • readOnly — focusable but cannot be toggled (default false)
  • required — must be on before the owning form submits (default false)
  • name — field name for form submission
  • value / uncheckedValue — value submitted when on / off (defaults to the native checkbox behavior — "on" when checked, nothing when unchecked)
  • form — id of the owning <form>, for a switch rendered outside it
  • id — applied to the hidden <input>, for pairing with a <label htmlFor>
  • inputRef — ref to the hidden <input type="checkbox"> element
  • className — extend or override the track styling

Accessibility

  • Renders a <span> with role="switch" and aria-checked reflecting the state, alongside a hidden native <input type="checkbox"> that carries the value into form submission.
  • No implicit label — always pair the switch with a visible <label htmlFor> (matched to the switch’s id) or an aria-label. A bare switch is unannounced.
  • Focus is shown with the brand focus ring (focus-visible only), so pointer toggles stay quiet while keyboard users get a clear ring.

Keyboard:

  • Tab — move focus to the switch
  • Space / Enter — toggle it

disabled removes the switch from the tab order; readOnly keeps it focusable but rejects the toggle.

Best practices

  • Use a switch when the change applies the instant it’s flipped (dark mode, notifications). If the user must press Save for it to count, use a Checkbox.
  • Always give the switch a visible label or an aria-label — a lone switch is ambiguous.
  • Don’t pair a switch with an explicit Apply button; that contradicts its immediate-effect meaning.