Skip to content

Design system

Documentation

DesignComponents / Switch

Components / Switch

Stable

Switch

Use Switch for a binary setting that takes effect immediately. Keep the label specific, and let the surrounding feature own persistence and business rules.

On this page

Examples

On and off

A binary preference communicates its result through a clear label.

Receive a weekly summary of workspace activity.

Uncontrolled state with a default value.

Disabled while the workspace is in use.

Read-only state while inherited from the organisation.

Use a full sentence when the setting has a high-impact consequence.

Controlled, uncontrolled, disabled, read-only, and long-label switch states

Sizes

Use sm, md, or lg to match the surrounding form density.

sm
md
lg

Small, medium, and large switch sizes

Icon in the thumb

Reinforce the on and off meaning with an icon that moves with the thumb.

Switch with a sun and moon icon inside the thumb

Controlled state

Feature state updates immediately when the switch changes.

Controlled Switch

Uncontrolled default

defaultChecked supplies a local initial value without mirrored state.

Uncontrolled Switch with a default value

Disabled and read-only

Unavailable and inherited states remain understandable without relying on color.

Disabled and read-only Switch states

Native form value

name, value, and required integrate with a native form when needed.

Switch with native form attributes

Switch playground

Change a prop and the live preview and copied example stay in sync.

Switch preview with the selected props

When to use

Use Switch when the setting has two states and can be changed without an additional submit step. Use a checkbox when several options are submitted together, and use a radio group when the person must choose one option from several.

Usage

switch-basic.tsx
"use client";

import { useState } from "react";

import { Field, FieldDescription, FieldLabel } from "@phuctech/ui/components/field";
import { Switch } from "@phuctech/ui/components/switch";

export function Example() {
  const [emailUpdates, setEmailUpdates] = useState(true);

  return (
    <Field>
      <FieldLabel htmlFor="email-updates">Email updates</FieldLabel>
      <Switch
        id="email-updates"
        checked={emailUpdates}
        onCheckedChange={setEmailUpdates}
        aria-describedby="email-updates-description"
      />
      <FieldDescription id="email-updates-description">
        Receive a weekly summary of workspace activity.
      </FieldDescription>
    </Field>
  );
}

Controlled state is the right choice when the feature saves the setting, reflects a server value, or needs to coordinate other UI. Use defaultChecked for a local uncontrolled preference. The Switch component does not read or write theme state on its own.

States

Switch states and guidance
StateGuidance
checkedControlled on/off state. Pair the setting with a label that describes the result.
defaultCheckedInitial value for an uncontrolled switch; do not change between controlled and uncontrolled models after mount.
disabledUse when the setting cannot change in the current context; explain a non-obvious reason nearby.
readOnlyKeep the current value visible and focusable while preventing changes.

API

Switch wraps Base UI's Switch.Root and renders its thumb. It forwards the native and Base UI props below; the thumb is part of the component and is not supplied as children.

Component API
PropTypeDefaultDescriptionConstraints
size"sm" | "md" | "lg""md"Control size of the track and thumb.Match the surrounding form density; md is the default.
checkedIconReactNodeIcon shown inside the thumb while the switch is on.Keep it decorative (aria-hidden); the switch still needs a label.
uncheckedIconReactNodeIcon shown inside the thumb while the switch is off.Pair with checkedIcon so both states read clearly.
checkedbooleanControlled on/off state.Pair with onCheckedChange and keep the state in the feature.
defaultCheckedbooleanfalseInitial value for an uncontrolled switch.Do not combine with checked.
onCheckedChange(checked, eventDetails) => voidCalled after the user requests a new state.Use it to update controlled state or trigger feature logic.
disabledbooleanfalsePrevents interaction and communicates unavailable state.Explain unexpected unavailability nearby.
readOnlybooleanfalsePrevents changes while keeping the value available.Use when the value is inherited or temporarily locked.
idstringID for label association and the hidden form input.Use with FieldLabel or a native label.
name / value / uncheckedValuestringForm submission values.Use name when the switch belongs to a native form.
required / formboolean / stringfalse / —Native form semantics.Use required only when the setting must be enabled to submit.
aria-label / aria-labelledby / aria-describedbystringAccessible name and supporting description.Every switch needs a visible label or an explicit accessible name.
className / refstring / Ref<HTMLElement>Style extension and access to the root control.Keep custom styling on semantic token roles.
renderReactElement | ComponentRenderFnspanBase UI composition hook for the root control.Keep the switch semantics and thumb when composing.

Tokens

Component tokens
TokenRoleEffective valueUsage
size scale (sm / md / lg)Switch track and thumb size1rem / 1.25rem / 1.5rem track heightPick the size that matches the surrounding form density; md is the default.
--color-action-primary / --color-bg-subtleOn/off tracksemantic rolesChecked and unchecked states remain distinct in both themes.
--color-border-strongUnchecked boundarysemantic borderPreserves the off-state edge on a light surface.
--focus-ring-*Keyboard focus3px / 2px / semantic focusThe shared focus ring remains visible around the track.
--motion-duration-normalThumb movement180msThe state change is clear and respects prefers-reduced-motion.

Accessibility

  • Give every switch a visible label through FieldLabel or a concise aria-label.
  • Connect supporting instructions with aria-describedby; do not rely on the track color alone.
  • The control is keyboard-operable with Space and exposes its state through the native switch role.
  • Keep disabled and read-only states understandable in both light and dark themes, and preserve the visible focus ring.

Do and don’t

Do

Use a verb-led label such as “Email updates”, keep the state local to the feature, and provide a description when the consequence is not obvious.

Don’t

Use a switch for navigation, hide the only label in a tooltip, or make a setting appear changed before its state is known.

  • Field connects a label and description to the control.
  • Colors documents the semantic roles used for both themes.
  • Accessibility documents focus, contrast, and reduced-motion checks.