Primitives
Primitive

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.

ColorDescription
primaryThe default, your brand colour.
neutralA 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.

ShapeDescription
circleThe default, a round thumb.
pillA wider, rectangular thumb.
squircleA 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.

SizeThumbTrack heightWhere it fits
xs14px18pxDense rows: menu items, toolbars.
sm16px20pxCompact settings rows and cards.
default20px24pxStandalone 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.

MotionDescription
defaultThe thumb slides across at a constant width.
stretchThe 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.

Prop

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.