HextaUI

Checkbox

チェックが描かれるように表示されるチェックボックスです。中間状態の親、グループ、ホバーを共有するラベルに対応します。

import { Checkbox } from "@/components/ui/checkbox"

export function CheckboxDemo() {
  return (
    <div className="flex flex-col gap-3">
      <label className="flex items-center gap-3 text-sm">
        <Checkbox />
        Email me about product updates
      </label>
      <label className="flex items-center gap-3 text-sm">
        <Checkbox defaultChecked />
        Remember this device
      </label>
      <label className="flex max-w-sm items-start gap-3 text-sm">
        <span className="flex h-5 items-center">
          <Checkbox />
        </span>
        <span className="flex flex-col gap-0.5">
          <span className="leading-5 font-medium">Mentions and replies</span>
          <span className="text-muted-foreground">
            We’ll only notify you about activity on your own posts.
          </span>
        </span>
      </label>
    </div>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/checkbox.json

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

import { Checkbox, CheckboxGroup } from "@/components/ui/checkbox"
<label className="flex items-center gap-3">
  <Checkbox defaultChecked />
  Remember this device
</label>
CheckboxGroup
└── Checkbox

状態

disabled、readOnly、indeterminate、aria-invalid。読み取り専用のボックスは値を保持してフォーカス可能なままですが、クリックとキー操作は無視します。

import { Checkbox } from "@/components/ui/checkbox"

export function CheckboxStates() {
  return (
    <div className="flex flex-col gap-3 text-sm">
      <label className="flex items-center gap-3">
        <Checkbox disabled />
        Disabled
      </label>
      <label className="flex items-center gap-3">
        <Checkbox disabled defaultChecked />
        Disabled and checked
      </label>
      <label className="flex items-center gap-3">
        <Checkbox readOnly defaultChecked />
        Read-only
      </label>
      <label className="flex items-center gap-3">
        <Checkbox indeterminate />
        Indeterminate
      </label>
      <label className="flex items-center gap-3">
        <Checkbox aria-invalid />
        Invalid
      </label>
      <label className="flex items-center gap-3">
        <Checkbox aria-invalid defaultChecked />
        Invalid and checked
      </label>
    </div>
  )
}

すべて選択

<CheckboxGroup /> の内側では、parent を持つチェックボックスが allValues のすべての値をチェックします。一部だけがチェックされている場合は、不確定状態になります。

"use client"

import * as React from "react"

import { Checkbox, CheckboxGroup } from "@/components/ui/checkbox"

const fruits = ["Apple", "Banana", "Cherry", "Mango", "Peach"]

export function CheckboxSelectAll() {
  const [value, setValue] = React.useState<string[]>(["Banana"])

  return (
    <div className="text-sm">
      <CheckboxGroup
        aria-label="Fruits"
        value={value}
        onValueChange={setValue}
        allValues={fruits}
      >
        <label className="flex items-center gap-3">
          <Checkbox parent />
          <span>
            Select all{" "}
            <span className="text-muted-foreground">
              ({value.length}/{fruits.length})
            </span>
          </span>
        </label>
        <div className="flex flex-col gap-3 ps-7">
          {fruits.map((fruit) => (
            <label key={fruit} className="flex items-center gap-3">
              <Checkbox value={fruit} />
              {fruit}
            </label>
          ))}
        </div>
      </CheckboxGroup>
    </div>
  )
}

ネストしたグループ

グループはネストできます。各親は直下のグループの状態を反映し、最上位の親はすべてを対象にします。

"use client"

import * as React from "react"

import { Checkbox, CheckboxGroup } from "@/components/ui/checkbox"

const userPermissions = ["users.read", "users.write", "users.delete"]
const billingPermissions = ["billing.read", "billing.write"]

export function CheckboxNestedGroups() {
  const [users, setUsers] = React.useState<string[]>(["users.read"])
  const [billing, setBilling] = React.useState<string[]>([])

  return (
    <div className="text-sm">
      <CheckboxGroup
        aria-label="Permissions"
        value={[...users, ...billing]}
        onValueChange={(next) => {
          setUsers(next.filter((value) => value.startsWith("users.")))
          setBilling(next.filter((value) => value.startsWith("billing.")))
        }}
        allValues={[...userPermissions, ...billingPermissions]}
      >
        <label className="flex items-center gap-3">
          <Checkbox parent />
          All permissions
        </label>
        <div className="flex flex-col gap-3 ps-7">
          <CheckboxGroup
            aria-label="Users"
            value={users}
            onValueChange={setUsers}
            allValues={userPermissions}
          >
            <label className="flex items-center gap-3">
              <Checkbox parent />
              Users
            </label>
            <div className="flex flex-col gap-3 ps-7">
              {userPermissions.map((permission) => (
                <label key={permission} className="flex items-center gap-3">
                  <Checkbox value={permission} />
                  {permission}
                </label>
              ))}
            </div>
          </CheckboxGroup>
          <CheckboxGroup
            aria-label="Billing"
            value={billing}
            onValueChange={setBilling}
            allValues={billingPermissions}
          >
            <label className="flex items-center gap-3">
              <Checkbox parent />
              Billing
            </label>
            <div className="flex flex-col gap-3 ps-7">
              {billingPermissions.map((permission) => (
                <label key={permission} className="flex items-center gap-3">
                  <Checkbox value={permission} />
                  {permission}
                </label>
              ))}
            </div>
          </CheckboxGroup>
        </div>
      </CheckboxGroup>
    </div>
  )
}

制御

状態を自分のコードで管理するには、checked と onCheckedChange を渡します。

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"

export function CheckboxControlled() {
  const [checked, setChecked] = React.useState(false)

  return (
    <div className="flex flex-col items-start gap-3">
      <label className="flex items-center gap-3 text-sm">
        <Checkbox checked={checked} onCheckedChange={setChecked} />
        Controlled ({checked ? "on" : "off"})
      </label>
      <Button variant="outline" size="sm" onClick={() => setChecked(!checked)}>
        Toggle from outside
      </Button>
    </div>
  )
}

Form

非表示の input が、ネイティブのチェックボックスと同様に name と value を送信し、required はチェックされるまで送信をブロックします。

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"

export function CheckboxForm() {
  const [submitted, setSubmitted] = React.useState<string>()

  return (
    <form
      className="flex flex-col items-start gap-3 text-sm"
      onSubmit={(event) => {
        event.preventDefault()
        const data = new FormData(event.currentTarget)
        setSubmitted(JSON.stringify(Object.fromEntries(data.entries())))
      }}
    >
      <label className="flex items-center gap-3">
        <Checkbox name="terms" required />I agree to the terms
      </label>
      <label className="flex items-center gap-3">
        <Checkbox name="newsletter" value="weekly" defaultChecked />
        Weekly newsletter
      </label>
      <Button type="submit" size="sm">
        Submit
      </Button>
      <output className="text-muted-foreground">
        {submitted ?? "Nothing submitted yet."}
      </output>
    </form>
  )
}

兄弟のラベル

ラベルでボックスを囲めない場合は、nativeButton を指定してチェックボックスを <button> としてレンダリングし、htmlFor でラベルをそれに向けます。

import { Checkbox } from "@/components/ui/checkbox"

export function CheckboxSiblingLabel() {
  return (
    <div className="flex items-center gap-2 text-sm">
      <Checkbox id="terms" nativeButton render={<button />} />
      <label htmlFor="terms">Accept terms and conditions</label>
    </div>
  )
}

カード

カード全体をラベルで囲むと、カードがヒット領域になります。スタイルは has-data-checked で指定します。

import { Checkbox, CheckboxGroup } from "@/components/ui/checkbox"

const addOns = [
  { value: "analytics", title: "Analytics", text: "Dashboards and exports." },
  { value: "backups", title: "Backups", text: "Daily snapshots, 30 days." },
]

export function CheckboxCards() {
  return (
    <CheckboxGroup
      aria-label="Add-ons"
      defaultValue={["analytics"]}
      className="w-full max-w-md"
    >
      <div className="grid gap-3 sm:grid-cols-2">
        {addOns.map((addOn) => (
          <label
            key={addOn.value}
            className="flex items-start gap-3 rounded-xl border p-4 text-sm transition-colors has-data-checked:border-primary"
          >
            <span className="flex h-5 items-center">
              <Checkbox value={addOn.value} />
            </span>
            <span className="flex flex-col gap-0.5">
              <span className="leading-5 font-medium">{addOn.title}</span>
              <span className="text-muted-foreground">{addOn.text}</span>
            </span>
          </label>
        ))}
      </div>
    </CheckboxGroup>
  )
}

長いコンテンツ

ボックスは 1 行目にとどまり、長いラベルや区切りのないテキストはその隣で折り返されます。

import { Checkbox } from "@/components/ui/checkbox"

export function CheckboxLongContent() {
  return (
    <label className="flex w-64 max-w-full items-start gap-3 text-sm">
      <span className="flex h-5 items-center">
        <Checkbox />
      </span>
      <span className="flex min-w-0 flex-col gap-0.5 wrap-anywhere">
        <span className="leading-5 font-medium">
          A label long enough to wrap onto a second and even a third line in a
          narrow container
        </span>
        <span className="text-muted-foreground">
          averyveryverylongunbrokenstringthatwouldotherwiseescapeitscontainer
        </span>
      </span>
    </label>
  )
}

右から左

ボックスは行頭側に配置され、ラベルは読む方向に従います。

import { Checkbox } from "@/components/ui/checkbox"

export function CheckboxRtl() {
  return (
    <div dir="rtl" className="flex flex-col gap-3 text-sm">
      <label className="flex items-center gap-3">
        <Checkbox defaultChecked />
        تذكر هذا الجهاز
      </label>
      <label className="flex max-w-sm items-start gap-3">
        <span className="flex h-5 items-center">
          <Checkbox />
        </span>
        <span className="flex flex-col gap-0.5">
          <span className="leading-5 font-medium">الإشارات والردود</span>
          <span className="text-muted-foreground">
            سنرسل لك إشعارات فقط حول نشاط منشوراتك.
          </span>
        </span>
      </label>
    </div>
  )
}
キーアクション
Spaceチェックボックスをチェックまたは解除します。
Enterネイティブのチェックボックスと同様に、チェックボックスが属するフォームを送信します。ボックスの状態を切り替えることはありません。
Tabフォーカスを次のチェックボックスに移動します。
  • チェックボックスとそのテキストを <label> で囲みます。ラベルがチェックボックスの名前になり、ラベルへのホバーや押下で、ボックス自体にホバーしたときと同じフィードバックがボックスに表示されます。
  • グループの用途をスクリーンリーダーが読み上げられるよう、すべての <CheckboxGroup /> に aria-label または aria-labelledby を付けてください。
  • ヒット領域は 16px のボックスの外側まで広がり、タッチスクリーンではさらに大きくなります。
  • チェックされるとチェックマークが描画されます。モーションの低減が有効な場合は、線のアニメーションなしで表示されます。

Base UI の checkbox と checkbox group をベースにしています。どちらも、ラップしているプリミティブの props を受け付けます。

プロパティ型デフォルト
checked
boolean–
defaultChecked
booleanfalse
onCheckedChange
(checked: boolean, details) => void–
indeterminateダッシュを表示します。チェック済みでも未チェックでもない状態です。
booleanfalse
disabled
booleanfalse
readOnlyフォーカスはできますが、値は変更できません。
booleanfalse
required
booleanfalse
nameチェックされているときにフォームと一緒に送信される値。
string–
valueグループ内でボックスを識別し、フォームが送信する値になります。name、それもなければ「on」にフォールバックします。
string–
uncheckedValueチェックされていないときに送信される値。デフォルトでは何も送信されません。
string–
parentグループの allValues にあるすべての値を制御します。CheckboxGroup の内側でのみ使えます。
booleanfalse
inputRef
Ref<HTMLInputElement>–
nativeButtonrender が <button> の場合は true に設定します。
booleanfalse
render
ReactElement | (props, state) => ReactElement<span>
属性説明
data-slot="checkbox"CSS でボックスを指定します。
data-checkedチェックされているときに付与されます。
data-uncheckedチェックされていないときに付与されます。
data-indeterminate不確定状態のときに付与されます。
data-disabled無効のときに存在します。
data-readonly読み取り専用のときに付与されます。
data-required必須のときに付与されます。
data-invalidBase UI の Field 内で無効なときに付与されます。
プロパティ型デフォルト
valueチェックされているチェックボックスの値。
string[]–
defaultValue
string[]–
onValueChange
(value: string[], details) => void–
allValuesグループ内のすべての値。親チェックボックスには必須です。
string[]–
disabled
booleanfalse
render
ReactElement | (props, state) => ReactElement<div>
属性説明
data-slot="checkbox-group"CSSでグループを指定します。
data-disabledグループが無効なときに付与されます。

使用しているブロック

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