Skip to contentVibraUI
Inputs & filters

Switch

An on/off toggle for a setting that applies at once.

Vibra moves the thumb and colours the track on the theme's motion tokens, focuses with the kit's solid accent outline, and keeps the unchecked track the opaque --input grey in both modes, where shadcn's goes translucent in the dark. Checked, the track is the accent through --primary, and the thumb travels to the inline end — left, on a right-to-left page. Label a switch with a statement that is true when it is on.

Install

npx shadcn@latest add @vibra/switch

Needs the @vibra registry in your components.json — set it up once.

Examples

Says what on means

The label is a statement true when the switch is on, and the line under it says what the current position means.

Disabled, with a reason

data-disabled on the Field dims the label with the switch; the reason and the way to change it keep their contrast.

Governs the ones under it

Off, the switches under it keep their positions but can't change, and say why — turning it back on restores them.

Waits for the server

Read-only while it saves, never disabled, so the focus stays; a refusal puts it back and says why in a danger Callout.

Asks before turning off

On is safe and immediate; off weakens every account, so it asks first, and either answer hands the focus back.

As a card

The whole card is the switch's label; on, it takes the kit's tint. The title names the switch and the line describes it.

In a table

size="sm" keeps the row at its height, and each switch is named for its row, because "Enabled" alone says nothing.

In a card's toolbar

A small switch beside its label that changes what the card shows.

Right to left

Arabic settings: the switch at the row's left end, and on means the thumb has travelled left.

Props

PropTypeDefaultDescription
size"sm" | "default""default"A 32 × 18 track, or 24 × 14 for a dense row.
checked / defaultCheckedboolean—On or off, controlled or not.
onCheckedChange(checked: boolean) => void—Called with the new position; a caller can hold it back, to confirm or to save first.
disabledbooleanfalseOut of the Tab order and dimmed; say why in text joined with aria-describedby.
readOnlybooleanfalseCan't be switched but keeps the focus — for the moment a change is being saved.

Dependencies

Source

components/ui/switch.tsx
"use client"

import { Switch as SwitchPrimitive } from "@base-ui/react/switch"
import { cn } from "@/lib/utils"

function Switch({
  className,
  size = "default",
  ...props
}: SwitchPrimitive.Root.Props & {
  size?: "sm" | "default"
}) {
  return (
    <SwitchPrimitive.Root
      data-slot="switch"
      data-size={size}
      className={cn(
        "peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-[background-color,border-color] duration-(--duration-fast) ease-(--ease-standard) focus-ring group-has-[:focus-visible]/field-label:border-transparent after:absolute after:-inset-x-3 after:-inset-y-2 aria-invalid:border-destructive data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] data-checked:bg-primary data-unchecked:bg-input data-disabled:cursor-not-allowed data-disabled:opacity-50",
        className
      )}
      {...props}
    >
      {/* The thumb starts at the inline start, so "on" travels towards the
          inline end: right in a left-to-right page, left in a right-to-left one. */}
      <SwitchPrimitive.Thumb
        data-slot="switch-thumb"
        className="pointer-events-none block rounded-full bg-background ring-0 transition-transform duration-(--duration-fast) ease-(--ease-standard) group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] rtl:group-data-[size=default]/switch:data-checked:-translate-x-[calc(100%-2px)] rtl:group-data-[size=sm]/switch:data-checked:-translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"
      />
    </SwitchPrimitive.Root>
  )
}

export { Switch }