affectUI › Components › Inputs

Switch

On or off for things that apply right away – label and second line, at the start or across a settings row, a thumb that stretches as you press it.

Free and open source (MIT). Install: npx shadcn@latest add https://www.affectui.com/r/switch.json · Import from @/components/ui/switch

When to use

  • Settings that apply the moment you flip them: notifications, features, a mode
  • Settings rows with the label on the left and the switch on the right – placement="end"

When not to use

  • Choices that are saved later with a form, or agreeing to something – use the Checkbox
  • More than two states – use the Segmented Control or the Select

Usage

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

export function DigestSetting({ onSave }: { onSave: (on: boolean) => void }) {
  const [on, setOn] = useState(true);

  return (
    <Switch
      placement="end"
      label="Daily digest"
      description="Approvals waiting, runs that finished or failed"
      checked={on}
      onChange={(e) => {
        setOn(e.target.checked);
        onSave(e.target.checked);
      }}
    />
  );
}

Props

NameTypeDefaultDescription
labelReactNodeShown next to the switch; clicking it toggles
descriptionReactNodeA quiet second line under the label, linked with aria-describedby
placement"start" | "end""start"start: the switch before the text · end: the text first and the switch on the far right of the row
size"sm" | "md""md"26 × 16 or 32 × 20; bigger on touch screens
...input propsComponentProps<"input">checked, defaultChecked, onChange, name, disabled, aria-label, ref – everything goes to the real input

Keyboard

  • Space: Turns it on or off
  • Tab: Moves to the next control

Accessibility

  • A real checkbox with role="switch": screen readers say on and off, and forms send it like a checkbox
  • The label is tied to it, so clicking the text toggles too; the description is linked with aria-describedby
  • Without a visible label, pass aria-label
  • The ring shows only under keyboard focus (:focus-visible), never after a click

Responsive

  • Long labels wrap; the switch stays on the first line
  • Touch screens get a 40 × 24 switch, 15px text and a hit area larger than the track
  • Dark mode built in: switches when an ancestor (usually <html>) has the class dark or data-theme="dark"

Requirements

  • React 19

Related

  • Checkbox – A real checkbox with a label and a second line, a some-of-them state for Select all, an error and a card row – the check draws itself
  • Segmented Control – Switch between two to six options – a raised pill or underline that glides, stretches a little and can be dragged across, with icons, counts and badges
  • Select – Pick one option in the Text Field's look – icons and a second line per option, a gliding highlight, type-ahead and a list that never gets cut off
  • Button – Primary, secondary, ghost and destructive buttons with icons, sizes and a shimmering loading state