NimUI
ComponentsPrimitives

Switch

Toggle switch for binary on/off states built on Radix UI

The Switch component is a toggle control for binary on/off states, built on Radix UI Switch. It provides an accessible, animated toggle that is ideal for settings and preferences where an immediate effect is expected.

Import

import { Switch } from '@nim-ui/components';

Variants

Default

Default Switch
Code
<div className="flex items-center gap-3">
  <Switch id="default-switch" />
  <label htmlFor="default-switch">Toggle me</label>
</div>

With Label Pattern

The recommended usage is to pair the Switch with a <label> element using matching id and htmlFor attributes.

Switch with Labels
Code
<div className="flex items-center gap-3">
  <Switch id="airplane-mode" />
  <label htmlFor="airplane-mode">Airplane Mode</label>
</div>
<div className="flex items-center gap-3">
  <Switch id="dark-mode" />
  <label htmlFor="dark-mode">Dark Mode</label>
</div>
<div className="flex items-center gap-3">
  <Switch id="notifications" />
  <label htmlFor="notifications">Notifications</label>
</div>

Sizes

Three size options are available: small, medium (default), and large.

Switch Sizes
Code
<div className="flex items-center gap-3">
  <Switch size="sm" id="switch-sm" />
  <label htmlFor="switch-sm">Small</label>
</div>
<div className="flex items-center gap-3">
  <Switch size="md" id="switch-md" />
  <label htmlFor="switch-md">Medium</label>
</div>
<div className="flex items-center gap-3">
  <Switch size="lg" id="switch-lg" />
  <label htmlFor="switch-lg">Large</label>
</div>

States

Disabled

Disabled State
Code
<div className="flex items-center gap-3">
  <Switch id="disabled-off" disabled />
  <label htmlFor="disabled-off">Disabled (off)</label>
</div>
<div className="flex items-center gap-3">
  <Switch id="disabled-on" disabled checked />
  <label htmlFor="disabled-on">Disabled (on)</label>
</div>

Props

NameTypeDefaultDescription
checkedboolean-Controlled checked state of the switch
onCheckedChange(checked: boolean) => void-Callback fired when the checked state changes
defaultCheckedboolean-Initial checked state for uncontrolled usage
disabledbooleanfalseWhether the switch is disabled
size'sm' | 'md' | 'lg''md'Size of the switch
idstring-HTML id attribute, used to associate with a label
classNamestring-Additional CSS classes to apply

Usage Examples

Settings Panel

function SettingsPanel() {
  const [settings, setSettings] = useState({
    darkMode: false,
    notifications: true,
    analytics: false,
    autoUpdate: true,
  });

  const toggle = (key: keyof typeof settings) => {
    setSettings((prev) => ({ ...prev, [key]: !prev[key] }));
  };

  return (
    <div className="space-y-6">
      <h3 className="text-lg font-semibold">Preferences</h3>
      <div className="space-y-4">
        <div className="flex items-center justify-between">
          <div>
            <label htmlFor="dark-mode" className="text-sm font-medium">
              Dark Mode
            </label>
            <p className="text-xs text-gray-500">Use dark theme throughout the app</p>
          </div>
          <Switch
            id="dark-mode"
            checked={settings.darkMode}
            onCheckedChange={() => toggle('darkMode')}
          />
        </div>
        <div className="flex items-center justify-between">
          <div>
            <label htmlFor="notifications" className="text-sm font-medium">
              Notifications
            </label>
            <p className="text-xs text-gray-500">Receive push notifications</p>
          </div>
          <Switch
            id="notifications"
            checked={settings.notifications}
            onCheckedChange={() => toggle('notifications')}
          />
        </div>
        <div className="flex items-center justify-between">
          <div>
            <label htmlFor="analytics" className="text-sm font-medium">
              Analytics
            </label>
            <p className="text-xs text-gray-500">Share anonymous usage data</p>
          </div>
          <Switch
            id="analytics"
            checked={settings.analytics}
            onCheckedChange={() => toggle('analytics')}
          />
        </div>
        <div className="flex items-center justify-between">
          <div>
            <label htmlFor="auto-update" className="text-sm font-medium">
              Auto Update
            </label>
            <p className="text-xs text-gray-500">Automatically install updates</p>
          </div>
          <Switch
            id="auto-update"
            checked={settings.autoUpdate}
            onCheckedChange={() => toggle('autoUpdate')}
          />
        </div>
      </div>
    </div>
  );
}

Feature Toggle

function FeatureToggle({ feature, enabled, onToggle }) {
  return (
    <div className="flex items-center justify-between p-4 border rounded-lg">
      <div>
        <h4 className="text-sm font-medium">{feature.name}</h4>
        <p className="text-xs text-gray-500">{feature.description}</p>
      </div>
      <Switch
        id={`feature-${feature.id}`}
        checked={enabled}
        onCheckedChange={onToggle}
        size="sm"
      />
    </div>
  );
}

Form Integration

function EmailPreferences() {
  const [prefs, setPrefs] = useState({
    marketing: false,
    product: true,
    security: true,
  });

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    savePreferences(prefs);
  };

  return (
    <form onSubmit={handleSubmit} className="space-y-4">
      <h3 className="text-lg font-semibold">Email Preferences</h3>
      <div className="flex items-center justify-between">
        <label htmlFor="marketing-email" className="text-sm">
          Marketing emails
        </label>
        <Switch
          id="marketing-email"
          checked={prefs.marketing}
          onCheckedChange={(checked) =>
            setPrefs((prev) => ({ ...prev, marketing: checked }))
          }
        />
      </div>
      <div className="flex items-center justify-between">
        <label htmlFor="product-email" className="text-sm">
          Product updates
        </label>
        <Switch
          id="product-email"
          checked={prefs.product}
          onCheckedChange={(checked) =>
            setPrefs((prev) => ({ ...prev, product: checked }))
          }
        />
      </div>
      <div className="flex items-center justify-between">
        <label htmlFor="security-email" className="text-sm">
          Security alerts
        </label>
        <Switch
          id="security-email"
          checked={prefs.security}
          onCheckedChange={(checked) =>
            setPrefs((prev) => ({ ...prev, security: checked }))
          }
          disabled
        />
      </div>
      <p className="text-xs text-gray-500">Security alerts cannot be disabled.</p>
      <Button type="submit">Save Preferences</Button>
    </form>
  );
}

Accessibility

The Switch component is built on Radix UI and follows WAI-ARIA best practices:

  • Uses role="switch" with proper aria-checked state
  • Supports aria-label and aria-labelledby for screen reader announcements
  • Full keyboard navigation support
  • Focus visible states for keyboard users
  • Disabled state properly communicated to assistive technology

Keyboard Support

KeyAction
SpaceToggles the switch on/off
EnterToggles the switch on/off
TabMoves focus to the next focusable element
Shift + TabMoves focus to the previous focusable element

Best Practices

  • Always pair switches with visible labels using id and htmlFor
  • Use Switch for settings that take immediate effect (no submit required)
  • Use Checkbox instead for form fields that require explicit submission
  • Provide descriptive labels that clearly indicate what is being toggled
  • Include supplementary text when the toggle effect is not obvious
  • Checkbox - Boolean selection for forms
  • Radio - Single selection from a group
  • Select - Dropdown selection
  • Button - Interactive button for actions

On this page