Switch

A switch is for a setting that applies as soon as it is flipped. A checkbox is for a choice you confirm later, with a submit button. If your control sits in a form with a Save at the bottom, it is a checkbox.

The component is called toggle() rather than switch() for a dull but unavoidable reason: switch is a reserved word, so it cannot be an import binding. Underneath it is a real <input type="checkbox"> carrying role="switch".

Basic switch

stack({ direction: 'row', gap: 'lg' },
  toggle({ label: 'Public site', name: 'public' }),
  toggle({ label: 'On', name: 'on', checked: true }),
)

Colors

stack({ direction: 'row', gap: 'lg', wrap: true },
  toggle({ label: 'Primary', checked: true, color: 'primary' }),
  toggle({ label: 'Neutral', checked: true, color: 'neutral' }),
  toggle({ label: 'Success', checked: true, color: 'success' }),
  toggle({ label: 'Warning', checked: true, color: 'warning' }),
  toggle({ label: 'Danger', checked: true, color: 'danger' }),
)

Disabled

stack({ direction: 'row', gap: 'lg' },
  toggle({ label: 'Off, and locked', disabled: true }),
  toggle({ label: 'On, and locked', checked: true, disabled: true }),
)

Without a label

A switch with no visible label still needs an accessible name. Pass aria-label — it falls through to the input.

toggle({ 'aria-label': 'Enable Pagefind search', checked: true })

A settings list

The usual shape: the label on the left, the switch on the right, one row per setting.

  • Pagefind search
    Indexes every page at the end of the build.
  • Image optimization
    Resizes and converts images at build time. Needs sharp.
  • Server islands
    Renders marked regions at request time.
return list(
  [
    ['Pagefind search', 'Indexes every page at the end of the build.', true],
    ['Image optimization', 'Resizes and converts images at build time. Needs sharp.', true],
    ['Server islands', 'Renders marked regions at request time.', false],
  ].map(([name, description, on]) =>
    listItem({
      title: name,
      description,
      end: toggle({ 'aria-label': name, checked: on }),
    }),
  ),
)

Props

PropTypeDefaultDescription
labelChildText beside the switch. Use aria-label when there is none.
color'primary' | 'neutral' | 'success' | 'warning' | 'danger''primary'Track colour when on.
checkedbooleanfalseWhether it starts on.
namestringForm field name.
disabledbooleanfalseDisables the input and dims the row.

Everything else falls through to the <input>, which is where onchange and aria-* belong.