Switch
A toggle switch component built on Radix UI for binary on/off states with accessible keyboard support.
Usage
Basic Switch
import { Switch } from "@prisma/eclipse";
export function BasicSwitch() {
return <Switch />;
}Live Example:
With Label
Use labels to make switches more accessible and user-friendly:
import { Switch } from "@prisma/eclipse";
export function SwitchWithLabel() {
return (
<div className="flex items-center gap-2">
<Switch id="airplane-mode" />
<label
htmlFor="airplane-mode"
className="text-sm font-medium leading-none cursor-pointer"
>
Airplane Mode
</label>
</div>
);
}Live Example:
With Field Component
Use the Field component for proper form structure with labels and descriptions:
import { Switch, Field, FieldLabel, FieldDescription } from "@prisma/eclipse";
export function SwitchWithField() {
return (
<Field orientation="horizontal" className="justify-between">
<div className="flex flex-col gap-1">
<FieldLabel htmlFor="notifications">Push Notifications</FieldLabel>
<FieldDescription>Receive push notifications on your device.</FieldDescription>
</div>
<Switch id="notifications" />
</Field>
);
}Live Example:
Receive push notifications on your device.
Controlled Switch
Control the switch state with React state:
import { Switch } from "@prisma/eclipse";
import { useState } from "react";
export function ControlledSwitch() {
const [enabled, setEnabled] = useState(false);
return (
<div className="space-y-4">
<div className="flex items-center gap-2">
<Switch
id="controlled"
checked={enabled}
onCheckedChange={setEnabled}
/>
<label htmlFor="controlled" className="text-sm font-medium">
Feature enabled
</label>
</div>
<p className="text-sm text-foreground-neutral-weak">
Status: {enabled ? "On" : "Off"}
</p>
</div>
);
}Live Example:
Status: Off
Disabled State
import { Switch } from "@prisma/eclipse";
export function DisabledSwitch() {
return (
<div className="space-y-4">
<div className="flex items-center gap-2">
<Switch id="disabled-off" disabled />
<label
htmlFor="disabled-off"
className="text-sm font-medium opacity-70 cursor-not-allowed"
>
Disabled (off)
</label>
</div>
<div className="flex items-center gap-2">
<Switch id="disabled-on" disabled checked />
<label
htmlFor="disabled-on"
className="text-sm font-medium opacity-70 cursor-not-allowed"
>
Disabled (on)
</label>
</div>
</div>
);
}Live Example:
Settings Panel Example
Create a settings panel with multiple switches:
import { Switch, Field, FieldLabel, FieldDescription } from "@prisma/eclipse";
export function SettingsPanel() {
return (
<div className="space-y-6 max-w-md">
<h3 className="text-lg font-semibold">Notification Settings</h3>
<Field orientation="horizontal" className="justify-between">
<div className="flex flex-col gap-1">
<FieldLabel htmlFor="email-notifications">Email Notifications</FieldLabel>
<FieldDescription>Receive notifications via email.</FieldDescription>
</div>
<Switch id="email-notifications" defaultChecked />
</Field>
<Field orientation="horizontal" className="justify-between">
<div className="flex flex-col gap-1">
<FieldLabel htmlFor="push-notifications">Push Notifications</FieldLabel>
<FieldDescription>Receive push notifications on your devices.</FieldDescription>
</div>
<Switch id="push-notifications" />
</Field>
<Field orientation="horizontal" className="justify-between">
<div className="flex flex-col gap-1">
<FieldLabel htmlFor="sms-notifications">SMS Notifications</FieldLabel>
<FieldDescription>Receive notifications via text message.</FieldDescription>
</div>
<Switch id="sms-notifications" />
</Field>
</div>
);
}Live Example:
Notification Settings
Receive notifications via email.
Receive push notifications on your devices.
Receive notifications via text message.
Form Integration
import { Switch } from "@prisma/eclipse";
import { useState } from "react";
export function SwitchForm() {
const [settings, setSettings] = useState({
darkMode: false,
notifications: true,
analytics: false,
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
console.log("Settings:", settings);
};
return (
<form onSubmit={handleSubmit} className="space-y-6 max-w-md">
<h3 className="text-lg font-semibold">App Settings</h3>
<div className="flex items-center justify-between">
<label htmlFor="dark-mode" className="text-sm font-medium">
Dark Mode
</label>
<Switch
id="dark-mode"
checked={settings.darkMode}
onCheckedChange={(checked) =>
setSettings({ ...settings, darkMode: checked })
}
/>
</div>
<div className="flex items-center justify-between">
<label htmlFor="notifications-form" className="text-sm font-medium">
Enable Notifications
</label>
<Switch
id="notifications-form"
checked={settings.notifications}
onCheckedChange={(checked) =>
setSettings({ ...settings, notifications: checked })
}
/>
</div>
<div className="flex items-center justify-between">
<label htmlFor="analytics-form" className="text-sm font-medium">
Analytics
</label>
<Switch
id="analytics-form"
checked={settings.analytics}
onCheckedChange={(checked) =>
setSettings({ ...settings, analytics: checked })
}
/>
</div>
<button
type="submit"
className="px-4 py-2 bg-primary text-primary-foreground rounded-md"
>
Save Settings
</button>
</form>
);
}Live Example:
Component Props
Switch
The Switch component extends all Radix UI Switch props:
checked- Controlled checked state (boolean, optional)defaultChecked- Default checked state for uncontrolled usage (boolean, optional)onCheckedChange- Callback when checked state changes ((checked: boolean) => void, optional)disabled- Whether the switch is disabled (boolean, default: false)required- Whether the switch is required (boolean, default: false)name- Name for form submission (string, optional)value- Value for form submission (string, default: "on")id- HTML id attribute (string, optional)className- Additional CSS classes (string, optional)- All standard Radix UI Switch Root props
Features
- ✅ Built on Radix UI for robust accessibility
- ✅ Keyboard navigation support (Space/Enter to toggle)
- ✅ Controlled and uncontrolled modes
- ✅ Disabled state
- ✅ Smooth animations and transitions
- ✅ Focus visible indicators with ring
- ✅ Works with forms
- ✅ Screen reader support
- ✅ Customizable with className
- ✅ Fully typed with TypeScript
- ✅ Dark mode support
Best Practices
- Always pair switches with labels for better UX and accessibility
- Use
htmlForon labels to match switchidfor clickable labels - Use switches for immediate state changes (like enabling/disabling features)
- Use checkboxes for form submissions that require confirmation
- Position labels to the left of switches in settings panels
- Use Field components for forms with descriptions
- Provide clear, concise label text that describes the "on" state
- Use disabled state sparingly and only when necessary
- Consider showing immediate feedback when state changes
- Test keyboard navigation (Tab, Space, Enter)
- Ensure labels clearly communicate what the switch controls
Common Use Cases
The Switch component is perfect for:
- Settings toggles - Enable/disable application features
- Feature flags - Turn features on or off
- Notification preferences - Control notification channels
- Privacy settings - Manage privacy and data sharing
- Theme switching - Toggle dark/light mode
- Accessibility options - Enable assistive features
- Permission controls - Grant or revoke permissions
- Status toggles - Active/inactive states
- Display options - Show/hide UI elements
- Auto-save - Enable automatic saving
Switch vs Checkbox
Use Switch when:
- The change takes effect immediately
- Toggling a feature on/off
- Representing a binary state (enabled/disabled)
- In settings or preferences panels
Use Checkbox when:
- Part of a form that requires submission
- Selecting multiple items from a list
- Accepting terms or agreements
- The change requires confirmation
Accessibility
The Switch component follows accessibility best practices:
- Uses Radix UI's accessible switch primitive
- Proper ARIA attributes (
role="switch",aria-checked) - Keyboard accessible (Space/Enter to toggle, Tab to navigate)
- Focus visible indicators with ring
- Screen reader support with proper labels
- Disabled state prevents interaction
- Works with form validation
- Supports required attribute
- Proper focus management
- High contrast in both checked and unchecked states
- Smooth animations respect
prefers-reduced-motion
Keyboard Shortcuts
- Space or Enter - Toggle switch on/off
- Tab - Move focus to next element
- Shift + Tab - Move focus to previous element
Styling
The Switch component uses Eclipse design tokens:
- Unchecked background:
bg-input - Checked background:
bg-primary - Thumb:
bg-background(unchecked),bg-primary-foreground(checked in dark mode) - Focus ring:
ring-ring/50with 3px width - Border: Transparent with shadow
- Border radius: Fully rounded (
rounded-full) - Shadow:
shadow-xs
Customize by passing className:
<Switch className="data-[state=checked]:bg-green-500" />Integration with Other Components
Switch works well with:
- Field - Form structure with labels and descriptions
- FieldLabel - Accessible labels
- FieldDescription - Helper text
- Forms - Settings and preferences
- Cards - Feature toggles within cards
- Lists - Settings lists
- Dialogs - Preference dialogs
Form Integration
The Switch component works seamlessly with forms:
<form onSubmit={handleSubmit}>
<Switch name="notifications" value="enabled" defaultChecked />
<button type="submit">Submit</button>
</form>When checked, the form data will include: notifications=enabled
Animation
The Switch component includes smooth animations:
- Thumb slides horizontally when toggled
- Background color transitions smoothly
- Focus ring fades in/out
- All animations use CSS transitions
- Respects user's motion preferences
Dark Mode
The Switch component automatically adapts to dark mode:
- Unchecked state uses
bg-input/80in dark mode - Thumb uses
bg-foregroundwhen unchecked in dark mode - Checked state remains consistent across themes
- All colors use design tokens for automatic theme adaptation