Switch
Accessible toggle controls with tones, state content, loading, validation, and grouped values
Switch is a native checkbox presented as an on/off toggle. It supports controlled and uncontrolled values, three sizes, active-state tones, icons, track text, validation, loading, and read-only interaction. Use SwitchGroup when several switches share a heading, description, validation message, or value collection.
Basic switch
import { Switch } from '@mosmox/ui';
<Switch label="Notifications" />The visible label is associated with the native checkbox through the wrapping label element. Native checkbox props such as name, value, required, id, form, aria-label, and aria-describedby are forwarded.
Controlled and uncontrolled values
Use checked and onChange when the parent owns the value:
const [enabled, setEnabled] = useState(true);
<Switch
label="Auto-save"
checked={enabled}
onChange={(event) => setEnabled(event.target.checked)}
/>Use defaultChecked when the switch should manage its own state:
<Switch label="Compact mode" defaultChecked />onChange receives the native ChangeEvent<HTMLInputElement>. loading, disabled, and readOnly prevent a value change; when the switch is read-only, it remains focusable and its current value is still available to assistive technology.
Sizes, label position, and state content
<Switch label="Default" size="default" />
<Switch label="Small" size="sm" />
<Switch label="Extra small" size="xs" />
<Switch label="Label on the left" labelPosition="left" />
<Switch label="With state text" onLabel="On" offLabel="Off" />The default label position is right. onLabel and offLabel add visible state text without changing the native boolean value.
onIcon and offIcon render inside the thumb. Keep icons decorative because the switch's accessible name comes from label or the native aria-label:
import { Check, X } from '@mosmox-ui/icons';
<Switch
label="Sync enabled"
defaultChecked
onIcon={<Check size={11} />}
offIcon={<X size={11} />}
/>Active tones and status
tone controls the active track color and defaults to primary for backward compatibility. Available tones are primary, success, warning, and danger.
<Switch label="Primary" defaultChecked tone="primary" />
<Switch label="Saved" defaultChecked tone="success" />
<Switch label="Needs review" defaultChecked tone="warning" />
<Switch label="Destructive option" defaultChecked tone="danger" />Use status="success" or status="warning" when the switch needs a semantic success or warning treatment independent of its active tone. An error always takes precedence over status for validation styling and aria-invalid.
<Switch label="Preference saved" status="success" defaultChecked />
<Switch label="Review required" status="warning" defaultChecked />
<Switch
label="Save preference"
status="warning"
error="The preference could not be saved."
/>Loading, disabled, and read-only states
Set loading while an asynchronous preference update is in progress. Loading disables interaction and shows a token-colored spinner inside the white thumb; on/off icons and track text are hidden while it is active. loadingLabel adds visible status text while loading; if omitted, the status is announced as Loading.
<Switch label="Saving preference" checked loading loadingLabel="Saving…" />
<Switch label="Unavailable preference" disabled />
<Switch label="Audited preference" readOnly defaultChecked />Read-only switches are not disabled: they can receive focus and expose aria-readonly="true", but pointer and keyboard attempts do not change the value or call onChange.
Description and validation
<Switch
label="Share diagnostics"
description="Only anonymous diagnostic data is collected."
name="shareDiagnostics"
required
/>
<Switch
label="Accept terms"
error="You must accept the terms to continue."
/>Descriptions and errors are connected to the input through aria-describedby. An error also sets aria-invalid unless it is explicitly overridden with the native prop.
SwitchGroup
SwitchGroup provides a shared label, description, and error and can manage a collection of switch values. Give each grouped switch a distinct value; the group value is an array of selected values.
import { Switch, SwitchGroup } from '@mosmox/ui';
const [channels, setChannels] = useState(['email']);
<SwitchGroup
label="Notification channels"
description="Choose where updates may be delivered."
value={channels}
onChange={(nextValue) => setChannels(nextValue)}
>
<Switch label="Email" value="email" />
<Switch label="Push notifications" value="push" />
<Switch label="Weekly digest" value="digest" />
</SwitchGroup>Use defaultValue for an uncontrolled group. onChange receives the next string[] followed by the native input event:
<SwitchGroup
label="Workspace features"
defaultValue={['comments']}
onChange={(nextValue, event) => {
console.log(nextValue, event.target.value);
}}
>
<Switch label="Comments" value="comments" />
<Switch label="Mentions" value="mentions" />
</SwitchGroup>Group-level disabled and readOnly apply to all child switches. The group uses a native fieldset and legend; its description and error IDs are also included in each child switch's accessible description.
Accessibility and behavior
Switchpreserves native checkbox semantics and Space-key interaction;- the wrapping label associates visible text with the checkbox;
- controlled switches render the
checkedprop and notify changes throughonChange; - uncontrolled switches initialize from
defaultCheckedand update internally; - descriptions, errors, and group messages are announced through
aria-describedby; - loading and disabled switches are disabled native inputs;
- read-only switches remain focusable, expose
aria-readonly, and do not change or emitonChange; - status styling is suppressed when
erroris present so validation remains unambiguous; - icon and track content is decorative and must not be the only accessible name.
Theme tokens
Switches use shared theme tokens for text, surface, control border, focus, typography, and active tones:
| Token | Purpose |
|---|---|
--mm-color-primary | Default active track color. |
--mm-color-success | Success active track and status color. |
--mm-color-warning | Warning active track color. |
--mm-color-warning-text | Warning status text. |
--mm-color-danger | Danger active track color. |
--mm-color-danger-text | Error and danger status text. |
--mm-color-control-border | Inactive track and loader border. |
--mm-color-focus | Keyboard focus ring. |
--mm-color-surface | Thumb and track content contrast. |
Override these tokens globally or with ConfigProvider theme colors:
<ConfigProvider
theme={{
colors: {
primary: '#14532d',
success: '#16a34a',
warning: '#d97706',
warningText: '#b45309',
danger: '#dc2626',
},
}}
>
<Switch label="Custom theme" defaultChecked tone="success" />
</ConfigProvider>API Reference
Switch
| Property | Type | Default | Description |
|---|---|---|---|
label | ReactNode | — | Visible switch label. |
description | string | — | Supporting text below the switch. |
error | string | — | Validation message and error styling. |
size | default | sm | xs | default | Switch visual size. |
tone | primary | success | warning | danger | primary | Active track color. |
status | success | warning | — | Semantic success or warning treatment. Ignored visually when error is set. |
labelPosition | left | right | right | Side of the track where the label appears. |
onIcon | ReactNode | — | Content inside the thumb when checked. |
offIcon | ReactNode | — | Content inside the thumb when unchecked. |
onLabel | ReactNode | — | Visible text when checked. |
offLabel | ReactNode | — | Visible text when unchecked. |
loading | boolean | false | Disables interaction and shows a spinner. |
loadingLabel | ReactNode | — | Visible status text while loading. |
readOnly | boolean | false | Keeps focus and value visibility but prevents changes. |
checked | boolean | — | Controlled on/off state. |
defaultChecked | boolean | false | Initial uncontrolled state. |
onChange | ChangeEventHandler<HTMLInputElement> | — | Native change handler. |
disabled | boolean | false | Disables interaction. |
Switch also accepts supported native InputHTMLAttributes<HTMLInputElement> props. The type and size attributes are owned by the component.
SwitchGroup
| Property | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Switches in the group. Give each one a distinct value. |
label | ReactNode | — | Shared accessible group heading. |
description | string | — | Shared supporting text. |
error | string | — | Shared validation message. |
value | string[] | — | Controlled selected values. |
defaultValue | string[] | [] | Initial uncontrolled selected values. |
onChange | (value: string[], event) => void | — | Called with the next selected values and native event. |
name | string | generated | Native name applied to child switches. |
disabled | boolean | false | Disables all child switches. |
readOnly | boolean | false | Makes all child switches read-only. |