Switch

A control that allows the user to toggle between checked and not checked.

Basic
With Description
With Label
Disabled
Sizes

Installation

Install the dependencies

pnpm add cn github:sveltery/base

Copy the component

Copy the following files into src/lib/components/ui/switch.

src/lib/components/ui/switch/Switch.svelte
<script lang="ts">
  import { Switch as Primitive } from '@sveltery/base/switch';
  import { cn } from 'cn';
  import type { SwitchProps } from './types.js';
  let { checked = $bindable(), class: classProp, size = 'default', ...props }: SwitchProps = $props();
</script>
<Primitive.Root data-slot="switch" data-size={size} bind:checked {...props} class={cn('data-checked:bg-primary data-unchecked:bg-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 dark:data-unchecked:bg-input/80 shrink-0 rounded-full border border-transparent focus-visible:ring-3 aria-invalid:ring-3 group-has-[:focus-visible]/field-label:ring-0 group-has-[:focus-visible]/field-label:border-transparent data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] peer group/switch relative inline-flex items-center transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 data-disabled:cursor-not-allowed data-disabled:opacity-50', classProp)}>
  <Primitive.Thumb data-slot="switch-thumb" class="bg-background dark:data-unchecked:bg-foreground dark:data-checked:bg-primary-foreground rounded-full 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)] group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 pointer-events-none block ring-0 transition-transform" />
</Primitive.Root>
src/lib/components/ui/switch/types.ts
import type { SwitchRootProps } from '@sveltery/base/switch';
export type SwitchProps = SwitchRootProps & { size?: 'sm' | 'default' };
export type { SwitchRootState as SwitchState } from '@sveltery/base/switch';
src/lib/components/ui/switch/index.ts
export { default as Switch } from './Switch.svelte';
export type { SwitchProps, SwitchState } from './types.js';

Update the import paths

Change the imports to match your project setup.

Usage

<script lang="ts">
  import { Switch } from '$lib/components/ui/switch';
  import { Label } from '$lib/components/ui/label';
</script>
<div class="flex items-center gap-2">
  <Switch id="airplane-mode" />
  <Label for="airplane-mode">Airplane Mode</Label>
</div>

Checked state

Bind checked to read and write the state. defaultChecked is the state an unbound switch starts from, as on an HTML checkbox.

<script lang="ts">
  let enabled = $state(false);
</script>

<Switch bind:checked={enabled} />
<Switch defaultChecked />

Forms

Switch renders a hidden checkbox, so it submits with its form. A checked switch submits value (default on) under name.

<form method="POST">
  <Switch name="notifications" value="enabled" defaultChecked />
  <button type="submit">Save</button>
</form>

Sizes

size is sm or default.

<Switch size="sm" />

API reference

Switch is styled on top of the Sveltery Base Switch primitive, which owns its state, keyboard behavior, form value and accessibility. See the Base UI Switch documentation for the behavior it ports.