HextaUI

Switch

タップ、押下、ドラッグで切り替えられるオン/オフのトグル。つまみは指の下で伸び、保存向けの保留状態も任意で使えます。

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

コンポーネント、HextaUIのテーマトークン、依存するHextaUIコンポーネントを追加します。

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

押すと、つまみは反対側へ向かって伸びてから跳ねて移動するため、押した感触が伝わります。つまみをつかんでドラッグすることもでき、中央を過ぎると切り替わり、手前なら元に戻ります。

iOS

variant="ios" は、背の高いトラック、カプセル型のつまみ、オン時の緑を使います。押下、ドラッグ、保存は同じように動作します。

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>
  )
}

保存中

onCheckedChange から promise を返すと、制御されている場合でもスイッチはすぐに切り替わります。保存に400msを超えて時間がかかるときだけ、つまみにスピナーが表示されます。reject された場合、スイッチは元に戻って揺れます。

"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>
  )
}

オンとオフの印

icons はトラックにチェックとリングを追加するため、状態が色だけに頼りません。

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>
  )
}

サイズ

sm、default、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>
  )
}

フィールド内

Field を使うと、ラベルと説明がスイッチに関連付けられます。

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>
  )
}

無効と読み取り専用

readOnly は値を表示したままフォーカス可能にしますが、変更はできません。

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>
  )
}

制御

checked と 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>
  )
}

右から左

つまみは右から始まって左へスライドし、ドラッグもそれに従います。

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>
  )
}
キーアクション
SpaceEnterフォーカスされているスイッチをオンまたはオフにします。
  • スイッチは、ラベルで名前が付く switch role です。<label> で囲むか、Field を使ってください。
  • 保存中は aria-busy になり、新しい押下を無視します。
  • ヒット領域はスイッチより大きく、タッチではさらに大きくなります。
  • 視差効果の軽減が有効な場合、つまみは即座に動き、揺れもありません。
プロパティ型デフォルト
checked
boolean–
defaultChecked
booleanfalse
onCheckedChangepromise を返すと保存中の状態が表示されます。reject された場合、非制御のスイッチは元に戻ります。
(checked, details) => void | Promise–
variant
"default" | "ios""default"
size
"sm" | "default" | "lg""default"
iconsトラックにオンとオフの印を表示します。
booleanfalse
name
string–
value
string–
disabled
booleanfalse
readOnly
booleanfalse
required
booleanfalse
render
ReactElement | (props, state) => ReactElement<span>
属性説明
data-slot="switch"トラック。data-size と data-variant を持ちます。
data-slot="switch-thumb"つまみ。
data-checked / data-uncheckedスイッチがオンかどうか。
data-draggingつまみをドラッグしている間付きます。
data-pending返された promise が保留中の間付きます。
--switch-w / --switch-h / --switch-thumb / --switch-thumb-wトラックとつまみのサイズ。size によって設定されます。

使用しているブロック

Switch の上に構築されるブロック。