nori-ui

Switch

Binary on/off toggle with controlled and uncontrolled modes.

3.6 kBgzipped

At a glance

  • Renders with role="switch" on web; maps to RN <Switch> on native, so the platform's native motion + haptics carry over on iOS / Android.
  • Use Switch for immediate state flips (settings that take effect on toggle). Reach for Checkbox when the user is choosing items in a form they'll submit later.

Preview

Direction:
Locale:

checked and defaultChecked

Controlled vs. uncontrolled — same shape as Checkbox.

// Controlled — parent owns the truth
const [enabled, setEnabled] = useState(false);
<Switch checked={enabled} onChange={setEnabled} label="Email digest" />
 
// Uncontrolled — Switch tracks state itself
<Switch defaultChecked label="Sync on Wi-Fi only" />

label

Renders a clickable label next to the track. Pressing the label is the same target as pressing the track itself, with a wider hit area.

<Switch label="Show advanced options" />

Inline label vs Field

The label prop is the inline label (right of the track) — the control's own affordance. Use it for stand-alone toggles:

import { Switch } from '@nori-ui/core';
 
export const Example = () => (
    <Switch label="Email digests" />
);

For grouped settings with description and error, wrap in <Field>:

import { Field, Switch } from '@nori-ui/core';
 
export const Example = () => (
    <Field label="Notifications" description="Choose how you'd like to be notified.">
        <Switch label="Email digests" />
    </Field>
);

See Field for the full API. For custom layouts, use the compound API.

onChange

Fires with the next boolean. Pair with checked for controlled mode, omit for uncontrolled — the Switch calls it either way.

<Switch onChange={(next) => savePreference(next)} />

disabled

Greys out the track and blocks onChange. Forwarded as aria-disabled so assistive tech narrates "switch, disabled".

<Switch disabled label="Requires Pro plan" />

asChild

Renders a supplied element as the interactive root. Same composition pattern as Checkbox / Button — pass any focusable element to inherit the Switch's behavior while keeping your own DOM shape.

<Switch asChild>
    <Pressable>
        <YourCustomRow />
    </Pressable>
</Switch>

Accessibility props

When wrapping Switch in <Field>, the following attributes are injected onto the underlying control automatically:

  • id — matches the htmlFor of the label generated by Field.
  • aria-labelledby — references the Field.Label element's ID.
  • aria-describedby — references Field.Description and/or Field.Error when present.
  • aria-invalid — set when the enclosing Field has a non-null error.
  • aria-required — set when Field has required={true}.

On native, React Native equivalents are forwarded instead: accessibilityLabelledBy (mirrors aria-labelledby) and accessibilityDescribedBy (mirrors aria-describedby).

You can also pass any of these props explicitly when using Switch without a Field wrapper.

Native preview

Props

PropTypeDefaultDescription
accessibilityDescribedBystringReact Native accessibilityDescribedBy forwarded to the Pressable
accessibilityLabelledBystringReact Native accessibilityLabelledBy forwarded to the Pressable
aria-describedbystringaria-describedby forwarded to the Pressable
aria-invalidbooleanMarks the control as invalid — set by Field.Control when there is an error
aria-labelledbystringaria-labelledby forwarded to the Pressable
aria-requiredbooleanMarks the control as required — set by Field.Control
asChildboolean
checkedboolean
classNamestring
defaultCheckedbooleanfalse
disabledboolean
idstringDOM id / nativeID forwarded to the Pressable — used by Field.Control
labelstring
namestringHTML name attribute (web only)
onChange(next: boolean) => void
testIDstring

On this page

Preview theme