Switch
Toggle control for binary on/off states with smooth animations
A switch toggles a single setting on or off and applies the change immediately. Use it for instant preferences like notifications or dark mode, not for choices a user submits later with a form (reach for Checkbox there).
Preview
Installation
Usage
Examples
Card
Wrap the switch in a Label to make a whole card row toggle the control.
Color
Use color to set the checked track. The unchecked track is a translucent overlay and does not change.
| Color | Description |
|---|---|
primary | The default, your brand colour. |
neutral | A dark neutral, for switches that should not compete for attention. |
For anything outside the scale, set --switch-fill on the switch or any ancestor. The hover step is derived from whatever it resolves to, so a custom colour gets a matching hover without a second value:
Each preset also sets the thumb while checked, so neutral gets a dark thumb in dark mode where white would vanish. A custom fill keeps the white thumb, so set --switch-thumb when the fill is light. Qualify it with data-checked:, or the dark thumb also lands on the unchecked track and disappears into it:
Shapes
Use shape to set the thumb's silhouette, which the track follows.
| Shape | Description |
|---|---|
circle | The default, a round thumb. |
pill | A wider, rectangular thumb. |
squircle | A squared-off thumb, for a more mechanical look. |
squircle uses CSS corner-shape. Browsers without it fall back to a rounded square.
Sizes
Use size to set the thumb height, which drives the track dimensions.
| Size | Thumb | Track height | Where it fits |
|---|---|---|---|
xs | 14px | 18px | Dense rows: menu items, toolbars. |
sm | 16px | 20px | Compact settings rows and cards. |
default | 20px | 24px | Standalone form control. |
Dropdown Menu, Context Menu and Menubar use xs for their indicator="switch" items, so a switch inside one already matches the surrounding text.
For a size the scale does not cover, set --thumb-size. It also takes responsive variants, which the preset cannot:
Motion
Use motion to set what the thumb does while the switch toggles.
| Motion | Description |
|---|---|
default | The thumb slides across at a constant width. |
stretch | The leading edge crosses first, so the thumb spans the track and flattens. |
Hover and press behave the same either way: the thumb widens on hover, and widens further on press. Both respond to a group/switch ancestor, so Label className="group/switch" makes a whole row drive the thumb. Menu items already do this. The flattening that comes with a press is separate, and squash below controls it.
Squash
squash controls the flattening, and it is the one option that reaches both motions through different triggers. Under default the thumb flattens while pressed. Under stretch it flattens mid-travel, from how far it has spread, with no press involved. Turn it off and the thumb keeps a constant height in both.
SwitchVisual flips this default to false, since a menu row or any other host that owns the press leaves the indicator itself inert. Pass squash to opt back in.
To retune the depth rather than switch it off, set --switch-press-squash. That, --switch-hover-ext, --switch-press-ext and --switch-duration all accept variants, so a value can differ by breakpoint:
Form Integration
Use Field for labeling and form participation. See the Forms guide for more patterns.
Receive email notifications when someone mentions you.
API Reference
The Switch component is built on top of Base UI's Switch. All Base UI props are supported. The documentation below only covers custom props specific to our implementation.
For the complete Base UI API, see the Base UI Switch documentation.
Props
Switch
Toggle control for binary on/off states. Wraps Base UI's Switch.Root and Switch.Thumb.
Colour lives on color rather than a variant prop. The unchecked track uses --switch-track, a translucent overlay that adapts to whatever surface it sits on, so one colour works on a page, a Card, or a toolbar. Set --switch-track and --switch-track-hover to retune it.
The 2px inset and the track, thumb and travel dimensions are snapped to whole pixels with CSS round(), so a p-* class will not change them. The checked track's hover step uses relative color syntax (oklch(from ...)), which every current browser supports; older ones render the hovered track transparent, so add your own hover with a supports-* variant if you target them. The motion needs the @property registrations in switch.css, which the component imports itself: nothing to wire up, but drop that import and the switch toggles without animating.