Skip to content
75NeoUI

component · react

Switch

A track the thumb slides along, across five sizes and seven colors, with a label, a description and an icon per state.

switch

A switch is a track, a thumb that slides along it, and the text beside it. Like the Checkbox it is one component rather than a control and a label wired together by hand, so clicking the text flips the switch with no for attribute to keep in sync.

<Switch label="Airplane mode" />
<Switch label="Weekly digest" description="One email on Monday." color="success" />
<Switch label="Airplane mode" />
<Switch label="Weekly digest" description="One email on Monday." color="success" />

Reach for it over a Checkbox when the change takes effect immediately. A checkbox states an intention that something else will act on, usually a form being submitted; a switch is the action.

On and off

Neither state is a recipe variant. Ark writes data-state on the control and on the thumb, and the recipe styles itself off that attribute, so one resolved class string covers both states and flipping a switch never re-resolves it.

React takes checked with onCheckedChange, or defaultChecked to leave the state alone. Vue takes v-model:checked, with defaultChecked as the uncontrolled counterpart.

const [enabled, setEnabled] = useState(false);

<Switch
  label={enabled ? "On" : "Off"}
  checked={enabled}
  onCheckedChange={(details) => setEnabled(details.checked)}
/>;
<script setup lang="ts">
const enabled = ref(false);
</script>

<template>
  <Switch v-model:checked="enabled" :label="enabled ? 'On' : 'Off'" />
</template>

The thumb’s two icons

checkedIcon and uncheckedIcon ride the thumb, and both stay mounted. The recipe hides whichever the state does not call for, rather than the component picking one, because an uncontrolled switch never tells the component which state it is in.

<Switch label="Theme" checkedIcon={<Moon />} uncheckedIcon={<Sun />} />
<Switch label="Theme" :checked-icon="Moon" :unchecked-icon="Sun" />

Passing only one is fine. The other side of the flip then shows a plain thumb.

While something is in flight

loading spins whichever icon the thumb is showing and stops the switch responding, so a change that has to reach a server can show that it has not landed yet. loadingIcon replaces the spinner. Both icon slots hold it, and the recipe still shows the one the state calls for.

<Switch label="Saving" checked={enabled} loading />
<Switch v-model:checked="enabled" label="Saving" loading />

In a form

The switch renders a real hidden checkbox, so name and value submit with the form around it and form associates one rendered outside it. required and invalid mark it the way the Checkbox does, and invalid draws the track’s ring in the error color whatever the switch’s own color is.

Sizes line up on their own

Every size is the same three numbers. The track is the thumb plus its padding on both sides, and the thumb travels what is left. That is why a switch never has to be told how far to move, and why size alone keeps the geometry right.

The track’s own alignment is worth knowing about: container gives it a box of the label’s line height, so the switch lines up with the first line of a label rather than with the top of a two-line block with a description under it.

Slots

The same word names the recipe slot, the data-slot attribute and the key in ui.

  • base
  • container
  • control
  • thumb
  • checkedIcon
  • uncheckedIcon
  • wrapper
  • label
  • description

Variants

Read off the recipe, so these are the values that actually resolve. A Theme layer can change which one is the default; it cannot add a value.

color
primarysecondarysuccessinfowarningerrorneutral
size
xssmmdlgxl
loading
truefalse

Props

Taken by both adapters, read out of the component module. The same names and the same types work in React and Vue.

ui

SwitchUI

Per-slot class overrides.

color

"primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral"

Defaults to "primary"

size

"xs" | "sm" | "md" | "lg" | "xl"

Defaults to "md"

label

string

Text beside the track. Clicking it flips the switch.

description

string

A quieter second line under the label.

checkedIcon

ReactNode

Rides the thumb while the switch is on.

uncheckedIcon

ReactNode

Rides the thumb while the switch is off.

loading

boolean

Spins the thumb's icon, and stops the switch responding.

Defaults to "false"

loadingIcon

ReactNode

Replaces the default spinner.

disabled

boolean

readOnly

boolean

invalid

boolean

required

boolean

name

string

Submits the switch under this name inside a form.

value

string

The value submitted when on.

Defaults to "on"

form

string

Id of the form to submit with, for a switch rendered outside it.

React only

Also takes Omit<React.LabelHTMLAttributes<HTMLLabelElement>, "color" | "defaultChecked" | "form">, and Pick<SwitchRootProps, "checked" | "defaultChecked" | "onCheckedChange" | "ids">. A className among them reaches the base slot.