Switch

A toggle control for switching between on and off states.

Installation

bash
npx @smicolon/fasla-ui@latest add switch

Run init once per project before adding components. See Installation.

Preview

Variants

Two styles, as in Figma. solid, the default, fills the track, with the primary colour when on. outline keeps the track on the page background, draws it with a 1px line, and fills the thumb instead.

solid
outline

Sizes

Three sizes. md is the default. The track is 32, 40 or 48px wide, and the label stays the same size.

solid
outline

Layout

control-first, the default, puts the track before the label and lines the description up under the label. label-first puts the label at the start and the track at the end of a full-width row, for settings lists.

control-first
label-first

States

Focus is a real CSS state rather than a prop. Tab to a switch to see the ring. Disabled dims the whole switch, label and description included.

solid
outline

Accessibility

  • Renders a native <input type="checkbox" role="switch">, so Space toggles it and it submits with a form without extra code.
  • description is linked with aria-describedby; the track is hidden from assistive technology.
  • Without a label, pass an aria-label.
  • Direction is inherited from dir, never a prop. The thumb always moves towards the end of the line when the switch turns on.

Props

PropTypeDefaultDescription
variant"solid" | "outline""solid"A filled track or an outlined one.
layout"control-first" | "label-first""control-first"Track before the label, or after it.
size"sm" | "md" | "lg""md"Switch size.
checkedboolean—On state. Use it with onChange.
defaultCheckedbooleanfalseStarting state when you do not control it.
disabledbooleanfalseDisables the switch and dims it.
labelstring—Label text.
descriptionstring—Secondary line below the label.
namestring—Name submitted with the form.
onChange(event) => void—Called when the switch changes.
classNamestring—Extra classes for the component.

Usage

Basic switch

tsx
import { Switch } from "@/components/ui/switch"

export function AirplaneModeSwitch() {
  return <Switch name="airplane" aria-label="Airplane mode" />
}

With label

tsx
import { Switch } from "@/components/ui/switch"

export function AirplaneModeSwitch() {
  return <Switch name="airplane" label="Airplane mode" defaultChecked />
}

With description

tsx
import { Switch } from "@/components/ui/switch"

export function DarkModeSwitch() {
  return (
    <Switch
      name="dark-mode"
      label="Dark mode"
      description="Toggle dark mode on or off"
    />
  )
}

Outline style

tsx
import { Switch } from "@/components/ui/switch"

export function AutoSyncSwitch() {
  return <Switch name="auto-sync" variant="outline" label="Auto-sync" />
}

Label first, track at the end

tsx
import { Switch } from "@/components/ui/switch"

export function NotificationsSwitch() {
  return <Switch name="notifications" layout="label-first" label="Notifications" />
}

Different sizes

tsx
import { Switch } from "@/components/ui/switch"

export function SwitchSizes() {
  return (
    <div className="flex flex-col gap-4">
      <Switch size="sm" label="Small" />
      <Switch size="md" label="Medium" />
      <Switch size="lg" label="Large" />
    </div>
  )
}

Controlled

tsx
"use client"

import { useState } from "react"
import { Switch } from "@/components/ui/switch"

export function ControlledNotificationsSwitch() {
  const [enabled, setEnabled] = useState(true)

  return (
    <Switch
      name="notifications"
      label="Notifications"
      checked={enabled}
      onChange={(e) => setEnabled(e.target.checked)}
    />
  )
}

State: On