Switch
A toggle control for binary choices with smooth animations.
Overview
The Switch component provides a toggle control for binary choices. The track
lands two surface levels above its parent, unchecked hover follows the shared
surface hover system, and the thumb resets to surface-1 while keeping its
border two levels above the track.
Usage
import { Label } from "@tilt-legal/cubitt-components/label";
import { Switch } from "@tilt-legal/cubitt-components/switch";<Switch />Always associate switches with descriptive labels for better accessibility and to make label hover trigger the same interaction state as the switch.
<Label orientation="horizontal">
<Switch />
Enable notifications
</Label>URL State
Provide paramName to sync the switch state with the current URL.
<Label orientation="horizontal">
<Switch defaultChecked={false} paramName="notifications" />
Enable notifications
</Label>Examples
States
import { useState } from "react";
import { Label } from "@tilt-legal/cubitt-components/label";
import { Switch } from "@tilt-legal/cubitt-components/switch";
import {
ToggleGroup,
ToggleGroupItem,
} from "@tilt-legal/cubitt-components/toggle-group";
type SwitchState = "off" | "on" | "disabled" | "invalid";
const states = ["off", "on", "disabled", "invalid"] as const;
const [state, setState] = useState<SwitchState>("off");
const checked = state === "on";
<>
<Label orientation="horizontal">
<Switch
aria-invalid={state === "invalid" ? true : undefined}
checked={checked}
disabled={state === "disabled"}
onCheckedChange={(nextChecked) => {
setState(nextChecked ? "on" : "off");
}}
/>
{state}
</Label>
<PreviewFooter>
<ToggleGroup
aria-label="Switch state"
groupVariant="segmented"
multiple={false}
onValueChange={(value) => {
if (states.includes(value as SwitchState)) {
setState(value as SwitchState);
}
}}
value={state}
>
<ToggleGroupItem value="off">Off</ToggleGroupItem>
<ToggleGroupItem value="on">On</ToggleGroupItem>
<ToggleGroupItem value="disabled">Disabled</ToggleGroupItem>
<ToggleGroupItem value="invalid">Invalid</ToggleGroupItem>
</ToggleGroup>
</PreviewFooter>
</>;Default
import { Label } from "@tilt-legal/cubitt-components/label";
import { Switch } from "@tilt-legal/cubitt-components/switch";
export default function Component() {
return (
<Label orientation="horizontal">
<Switch />
Default switch
</Label>
);
}Sizes
import { Label } from "@tilt-legal/cubitt-components/label";
import { Switch } from "@tilt-legal/cubitt-components/switch";
export default function Component() {
return (
<div className="flex items-center gap-6">
<Label orientation="horizontal">
<Switch size="sm" />
Small
</Label>
<Label orientation="horizontal">
<Switch size="md" />
Medium
</Label>
<Label orientation="horizontal">
<Switch size="lg" />
Large
</Label>
</div>
);
}Checked by Default
import { Label } from "@tilt-legal/cubitt-components/label";
import { Switch } from "@tilt-legal/cubitt-components/switch";
export default function Component() {
return (
<Label orientation="horizontal">
<Switch defaultChecked />
Enabled by default
</Label>
);
}Disabled
import { Label } from "@tilt-legal/cubitt-components/label";
import { Switch } from "@tilt-legal/cubitt-components/switch";
export default function Component() {
return (
<div className="flex flex-col gap-4">
<Label orientation="horizontal">
<Switch disabled />
Disabled (off)
</Label>
<Label orientation="horizontal">
<Switch disabled defaultChecked />
Disabled (on)
</Label>
</div>
);
}With Description
import { Label } from "@tilt-legal/cubitt-components/label";
import { Switch } from "@tilt-legal/cubitt-components/switch";
export default function Component() {
return (
<Label orientation="horizontal" className="items-start gap-3">
<Switch />
<div className="grid gap-1.5">
<span className="font-medium">Marketing emails</span>
<p className="text-fg-2 text-sm">
Receive emails about new products, features, and more.
</p>
</div>
</Label>
);
}Settings Card
import { Card, CardContent } from "@tilt-legal/cubitt-components/card";
import { Label } from "@tilt-legal/cubitt-components/label";
import { Switch } from "@tilt-legal/cubitt-components/switch";
export default function Component() {
return (
<Label className="block" orientation="horizontal">
<Card className="w-full max-w-sm" hoverable>
<CardContent className="flex-row items-start gap-3 p-4!">
<Switch id="switch-1" size="sm" />
<div className="grid gap-1.5 font-normal">
<p className="font-medium text-sm leading-none">
Enable notifications
</p>
<p className="text-fg-2 text-sm">
You can enable or disable notifications at any time.
</p>
</div>
</CardContent>
</Card>
</Label>
);
}URL State
Sync a switch state with the URL. Try toggling the switch and refreshing the page or sharing the URL.
import { Label } from "@tilt-legal/cubitt-components/label";
import { Switch } from "@tilt-legal/cubitt-components/switch";
export default function Component() {
return (
<Label orientation="horizontal">
<Switch paramName="demo-notifications" defaultChecked={false} />
Enable notifications
</Label>
);
}API Reference
Switch
The root component for creating switches with multiple size variants.
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | - | Controlled checked state. |
defaultChecked | boolean | false | Initial checked state when uncontrolled. |
onCheckedChange | (checked: boolean, eventDetails: SwitchPrimitive.Root.ChangeEventDetails) => void | - | Called when the checked state changes. |
size | "sm" | "md" | "lg" | "md" | Switch size. |
disabled | boolean | false | Disables the switch. |
required | boolean | false | Marks the switch as required for form submission. |
name | string | - | Form field name. |
value | string | "on" | Form value when checked. |
id | string | - | HTML id for explicit label association. |
className | string | - | Additional classes for the switch root. |
children | React.ReactNode | <SwitchThumb /> | Custom thumb content. |
URL State Props
When provided with a paramName, the switch will sync its state with URL parameters via TanStack Router search params.
| Prop | Type | Default | Description |
|---|---|---|---|
paramName | string | - | URL parameter name for syncing state. |
paramValue | boolean | - | Controlled URL state value. |
onUrlValueChange | (checked: boolean | null) => void | - | Called when the URL value changes. |
paramClearOnDefault | boolean | true | Removes the URL parameter when value equals the default. |
paramDebounce | number | - | Debounce window for URL updates. |
paramThrottle | number | - | Throttle window for URL updates. |
SwitchThumb
The thumb component that slides within the switch track.
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "md" | "lg" | — | Override the size from parent Switch. |
className | string | — | Additional CSS classes for the thumb. |