Lattice components

Switch

Boolean toggle primitive that owns checked state, animates the thumb between ends of the track, and lets you choose whether the primitive or your component owns track color.

@lattice-ui/switch Stable direction import Switch depends on core , motion

Switch is the primitive for an on/off toggle: settings flips, feature enables, and any binary control. The root is the toggleable track that owns checked state, and the thumb slides between the two ends as the state changes. You decide whether the primitive animates the track color for you or leaves your own colors untouched.

Reach for Switch when a control is boolean, should toggle on activation, and wants a thumb that animates between off and on positions.

Import

import { Switch } from "@lattice-ui/switch";

Anatomy

Root is the toggleable track and is the only required part. Thumb is the sliding handle; include it whenever you want the moving indicator.

Switch anatomy

Switch anatomy
<Switch.Root>
<Switch.Thumb />
</Switch.Root>
PartRequiredResponsibility
Switch.RootyesThe track button: owns checked state, toggles on activation, and animates track color when it owns it.
Switch.ThumbnoThe handle that animates between the off and on ends of the track.

Example

A controlled switch with an app-owned track and a custom thumb, letting the primitive animate the track color.

MusicToggle.tsx
import { useState } from "@rbxts/react";
import { Switch } from "@lattice-ui/switch";
export function MusicToggle() {
const [enabled, setEnabled] = useState(true);
return (
<Switch.Root
checked={enabled}
onCheckedChange={setEnabled}
trackOnColor={Color3.fromRGB(86, 141, 255)}
trackOffColor={Color3.fromRGB(66, 73, 91)}
asChild
>
<frame
BackgroundColor3={Color3.fromRGB(66, 73, 91)}
BorderSizePixel={0}
Size={UDim2.fromOffset(48, 24)}
>
<uicorner CornerRadius={new UDim(1, 0)} />
<Switch.Thumb>
<frame
BackgroundColor3={Color3.fromRGB(240, 244, 252)}
BorderSizePixel={0}
Size={UDim2.fromOffset(20, 20)}
>
<uicorner CornerRadius={new UDim(1, 0)} />
</frame>
</Switch.Thumb>
</frame>
</Switch.Root>
);
}

How it behaves

Checked state

Switch.Root is controllable. Pass checked and onCheckedChange to control it, or defaultChecked to run uncontrolled (defaults to false). Activating the root toggles the state; when disabled, activation is ignored and the state cannot change. The default (non-asChild) root renders a textbutton whose Text reflects "On"/"Off".

The thumb

Switch.Thumb animates its Position between the two ends of the track as checked changes, with a small inset on each side. It reads the thumb’s Size (from the slotted child when asChild, otherwise a 16×16 default) to compute the checked-end position, so non-default thumb sizes still land correctly. Pass forceMount to keep the thumb mounted regardless of state.

Track color ownership

trackColorMode decides who writes the track’s BackgroundColor3:

  • "switch" — the primitive animates between trackOnColor, trackOffColor, and disabledTrackColor as state changes.
  • "consumer" — the primitive leaves the track color alone so your slotted/default colors stand.

When trackColorMode is unset, the default is "switch" for a non-asChild root, and "consumer" for an asChild root unless you pass any track color prop (which opts into "switch"). Setting trackColorMode explicitly always wins over that fallback.

Motion

Both the track color (when owned) and the thumb position animate with a short response settle, so toggling feels immediate but smooth rather than snapping instantly.

API reference

Switch.Root

Prop Type Description
checked boolean Controlled checked state. Pair with onCheckedChange.
defaultChecked boolean Initial checked state for uncontrolled usage. Defaults to false.
onCheckedChange (checked: boolean) => void Called when the checked state changes.
disabled boolean Prevents toggling and removes the switch from selection. Defaults to false.
trackColorMode "consumer" | "switch" Who owns track BackgroundColor3. Defaults to "switch" without asChild, or "consumer" with asChild unless a track color prop is provided.
trackOnColor Color3 Track color when checked (when the primitive owns color). Defaults to RGB(86, 141, 255).
trackOffColor Color3 Track color when unchecked (when the primitive owns color). Defaults to RGB(66, 73, 91).
disabledTrackColor Color3 Track color when disabled (when the primitive owns color). Defaults to RGB(103, 110, 128).
asChild boolean Merge the track behavior onto the single child element instead of rendering the default textbutton.
children React.ReactNode The track contents (typically a Switch.Thumb). Required when asChild is set.

Switch.Thumb

Prop Type Description
forceMount boolean Keeps the thumb mounted regardless of state. Defaults to false.
asChild boolean Merge the animated thumb onto the single child element instead of rendering the default frame; the child's Size is used to compute the checked position.
children React.ReactNode The thumb contents. Required when asChild is set.