HextaUI

Switch

Un interruptor de activado/desactivado que puedes tocar, pulsar o arrastrar, con un thumb que se estira bajo tu dedo y un estado pendiente opcional para los guardados.

import { Switch } from "@/components/ui/switch"

const settings = [
  {
    id: "wifi",
    title: "Wi-Fi",
    description: "Join known networks automatically.",
    on: true,
  },
  {
    id: "bluetooth",
    title: "Bluetooth",
    description: "Connect to headphones and keyboards.",
    on: false,
  },
  {
    id: "airdrop",
    title: "Nearby sharing",
    description: "Let people around you send files.",
    on: true,
  },
]

export function SwitchDemo() {
  return (
    <div className="flex w-full max-w-sm flex-col divide-y rounded-xl border">
      {settings.map((setting) => (
        <label
          key={setting.id}
          className="flex items-center justify-between gap-4 p-4 text-sm"
        >
          <span className="flex flex-col gap-0.5">
            <span className="font-medium">{setting.title}</span>
            <span className="text-muted-foreground">{setting.description}</span>
          </span>
          <Switch defaultChecked={setting.on} />
        </label>
      ))}
    </div>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/switch.json

Añade el componente, los tokens del tema de HextaUI y los componentes de HextaUI de los que depende.

import { Switch } from "@/components/ui/switch"
<label className="flex items-center gap-3">
  <Switch defaultChecked />
  Wi-Fi
</label>

Al pulsar, el thumb se estira hacia el otro lado antes de saltar con un spring, para que sientas aterrizar la pulsación. También puedes agarrar el thumb y arrastrarlo: pasada la mitad cambia, y si no llega vuelve atrás.

iOS

variant="ios" usa una pista más alta, un thumb en forma de cápsula y verde cuando está activado. Pulsar, arrastrar y guardar funcionan igual.

import { Switch } from "@/components/ui/switch"

export function SwitchIos() {
  return (
    <div className="flex flex-col gap-4 text-sm">
      <label className="flex items-center gap-3">
        <Switch variant="ios" />
        Airplane mode
      </label>
      <label className="flex items-center gap-3">
        <Switch variant="ios" defaultChecked />
        Wi-Fi
      </label>
      <div className="flex items-center gap-4">
        <Switch variant="ios" size="sm" aria-label="Small" defaultChecked />
        <Switch variant="ios" size="lg" aria-label="Large" defaultChecked />
      </div>
    </div>
  )
}

Guardando

Devuelve una promesa desde onCheckedChange y el switch cambia de inmediato, incluso cuando es controlado. Un spinner aparece en el thumb solo si guardar tarda más de 400ms. Si se rechaza, el switch vuelve atrás y vibra.

"use client"

import { Switch } from "@/components/ui/switch"

function save(succeed: boolean) {
  return new Promise<void>((resolve, reject) =>
    setTimeout(() => (succeed ? resolve() : reject(new Error("Offline"))), 1200)
  )
}

export function SwitchAsync() {
  return (
    <div className="flex flex-col gap-4 text-sm">
      <label className="flex items-center gap-3">
        <Switch onCheckedChange={() => save(true)} />
        Sync to cloud (saves)
      </label>
      <label className="flex items-center gap-3">
        <Switch defaultChecked onCheckedChange={() => save(false)} />
        Public profile (fails and flips back)
      </label>
    </div>
  )
}

Marcas de activado y desactivado

icons añade una marca de verificación y un anillo a la pista, para que el estado no dependa del color.

import { Switch } from "@/components/ui/switch"

export function SwitchIcons() {
  return (
    <div className="flex flex-col gap-4 text-sm">
      <label className="flex items-center gap-3">
        <Switch icons defaultChecked />
        Captions
      </label>
      <label className="flex items-center gap-3">
        <Switch icons size="lg" />
        Reduce transparency
      </label>
    </div>
  )
}

Tamaños

sm, default y lg.

import { Switch } from "@/components/ui/switch"

export function SwitchSizes() {
  return (
    <div className="flex items-center gap-6">
      <Switch size="sm" aria-label="Small" defaultChecked />
      <Switch aria-label="Default" defaultChecked />
      <Switch size="lg" aria-label="Large" defaultChecked />
    </div>
  )
}

En un field

Con Field, la etiqueta y la descripción quedan enlazadas al switch.

import {
  Field,
  FieldContent,
  FieldDescription,
  FieldLabel,
} from "@/components/ui/field"
import { Switch } from "@/components/ui/switch"

export function SwitchField() {
  return (
    <Field orientation="horizontal" className="w-full max-w-sm">
      <FieldContent>
        <FieldLabel>Marketing emails</FieldLabel>
        <FieldDescription>
          Product news and offers, about once a month.
        </FieldDescription>
      </FieldContent>
      <Switch name="marketing" />
    </Field>
  )
}

Deshabilitado y de solo lectura

readOnly mantiene el valor visible y enfocable, pero no lo cambia.

import { Switch } from "@/components/ui/switch"

export function SwitchStates() {
  return (
    <div className="flex flex-col gap-4 text-sm">
      <label className="flex items-center gap-3">
        <Switch disabled />
        Disabled
      </label>
      <label className="flex items-center gap-3">
        <Switch disabled defaultChecked />
        Disabled and on
      </label>
      <label className="flex items-center gap-3">
        <Switch readOnly defaultChecked />
        Read-only (managed by your admin)
      </label>
    </div>
  )
}

Controlado

Pasa checked y onCheckedChange.

"use client"

import * as React from "react"

import { Switch } from "@/components/ui/switch"

export function SwitchControlled() {
  const [dark, setDark] = React.useState(false)

  return (
    <label className="flex items-center gap-3 text-sm">
      <Switch checked={dark} onCheckedChange={setDark} />
      Dark mode is {dark ? "on" : "off"}
    </label>
  )
}

De derecha a izquierda

El thumb empieza a la derecha y se desliza a la izquierda, y el arrastre lo sigue.

import { Switch } from "@/components/ui/switch"

export function SwitchRtl() {
  return (
    <div dir="rtl" className="flex flex-col gap-4 text-sm">
      <label className="flex items-center gap-3">
        <Switch defaultChecked icons />
        الإشعارات
      </label>
      <label className="flex items-center gap-3">
        <Switch />
        الوضع الداكن
      </label>
    </div>
  )
}
KeyAcción
SpaceEnterActiva o desactiva el switch enfocado.
  • El switch tiene el rol switch con el nombre de su etiqueta. Envuélvelo en un <label> o usa Field.
  • Mientras guarda, tiene aria-busy e ignora nuevas pulsaciones.
  • El área de toque es mayor que el switch, y aún mayor en táctil.
  • Con movimiento reducido, el thumb se mueve al instante y nada vibra.
PropTipoPredeterminado
checked
boolean–
defaultChecked
booleanfalse
onCheckedChangeDevuelve una promesa para mostrar el estado de guardado. Si se rechaza, un switch no controlado vuelve atrás.
(checked, details) => void | Promise–
variant
"default" | "ios""default"
size
"sm" | "default" | "lg""default"
iconsMuestra marcas de activado y desactivado en la pista.
booleanfalse
name
string–
value
string–
disabled
booleanfalse
readOnly
booleanfalse
required
booleanfalse
render
ReactElement | (props, state) => ReactElement<span>
AtributoDescripción
data-slot="switch"La pista, con data-size y data-variant.
data-slot="switch-thumb"El thumb.
data-checked / data-uncheckedSi el switch está activado.
data-draggingPresente mientras se arrastra el thumb.
data-pendingPresente mientras una promesa devuelta está pendiente.
--switch-w / --switch-h / --switch-thumb / --switch-thumb-wTamaños de la pista y del thumb, definidos por size.

Usado en bloques

Bloques que se construyen sobre Switch.