HextaUI

Checkbox

Eine Checkbox, deren Häkchen sich einzeichnet, mit unbestimmten übergeordneten Elementen, Gruppen und Labels, die ihren Hover teilen.

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

Fügt die Komponente, die HextaUI-Theme-Tokens und alle HextaUI-Komponenten hinzu, von denen sie abhängt.

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

Zustände

disabled, readOnly, indeterminate und aria-invalid. Eine schreibgeschützte Box behält ihren Wert und bleibt fokussierbar, ignoriert aber Klicks und Tasten.

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

Alle auswählen

In einer <CheckboxGroup /> hakt eine Checkbox mit parent jeden Wert in allValues an. Sie wird unbestimmt, wenn nur einige angehakt sind.

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

Verschachtelte Gruppen

Gruppen lassen sich verschachteln. Jede übergeordnete spiegelt den Zustand der Gruppe direkt unter ihr, und die oberste deckt alles ab.

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

Kontrolliert

Übergib checked und onCheckedChange, um den State in deinem eigenen Code zu halten.

"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

Ein verstecktes Input sendet name und value wie eine native Checkbox, und required blockiert das Absenden, bis sie angehakt ist.

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

Benachbartes Label

Wenn das Label die Box nicht umschließen kann, rendere die Checkbox mit nativeButton als <button> und verweise das Label per htmlFor darauf.

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

Karten

Umschließe eine ganze Karte mit dem Label, damit die Karte die Trefferfläche ist, und style sie mit 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>
  )
}

Langer Inhalt

Die Box bleibt in der ersten Zeile, während ein langes Label und Text ohne Umbruchstelle daneben umbrechen.

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

Rechts nach links

Die Box sitzt auf der Inline-Start-Seite, und das Label folgt der Leserichtung.

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>
  )
}
TasteAktion
SpaceHakt die Checkbox an oder ab.
EnterSendet das Formular ab, zu dem die Checkbox gehört, wie eine native Checkbox. Es schaltet die Box nie um.
TabSetzt den Fokus auf die nächste Checkbox.
  • Umschließe die Checkbox und ihren Text mit einem <label>. Das Label benennt die Checkbox, und Hovern oder Drücken gibt der Box dasselbe Feedback wie das Hovern der Box selbst.
  • Gib jeder <CheckboxGroup /> ein aria-label oder aria-labelledby, damit Screenreader ansagen, wofür die Gruppe gedacht ist.
  • Die Trefferfläche reicht über die 16-px-Box hinaus und wird auf Touchscreens größer.
  • Der Haken wird beim Anhaken gezeichnet. Bei reduzierter Bewegung erscheint er ohne Strichanimation.

Basiert auf der Base UI Checkbox und Checkbox Group. Beide akzeptieren die Props der Primitive, die sie umschließen.

PropTypStandard
checked
boolean–
defaultChecked
booleanfalse
onCheckedChange
(checked: boolean, details) => void–
indeterminateZeigt einen Strich: weder angehakt noch nicht angehakt.
booleanfalse
disabled
booleanfalse
readOnlyFokussierbar, aber der Wert lässt sich nicht ändern.
booleanfalse
required
booleanfalse
nameWird mit dem Formular gesendet, wenn angehakt.
string–
valueIdentifiziert die Box in einer Gruppe und ist das, was das Formular absendet. Fällt auf name zurück, dann auf „on“.
string–
uncheckedValueWird gesendet, wenn nicht angehakt. Standardmäßig nichts.
string–
parentSteuert jeden Wert in allValues der Gruppe. Nur innerhalb einer CheckboxGroup.
booleanfalse
inputRef
Ref<HTMLInputElement>–
nativeButtonAuf true setzen, wenn render ein <button> ist.
booleanfalse
render
ReactElement | (props, state) => ReactElement<span>
AttributBeschreibung
data-slot="checkbox"Die Box in CSS ansprechen.
data-checkedVorhanden, wenn angehakt.
data-uncheckedVorhanden, wenn nicht angehakt.
data-indeterminateVorhanden, wenn unbestimmt.
data-disabledVorhanden, wenn deaktiviert.
data-readonlyVorhanden, wenn schreibgeschützt.
data-requiredVorhanden, wenn erforderlich.
data-invalidVorhanden, wenn ungültig innerhalb eines Base UI Field.
PropTypStandard
valueWerte der angehakten Checkboxen.
string[]–
defaultValue
string[]–
onValueChange
(value: string[], details) => void–
allValuesJeder Wert der Gruppe. Für eine übergeordnete Checkbox erforderlich.
string[]–
disabled
booleanfalse
render
ReactElement | (props, state) => ReactElement<div>
AttributBeschreibung
data-slot="checkbox-group"Die Gruppe in CSS ansprechen.
data-disabledVorhanden, wenn die Gruppe deaktiviert ist.

In Blocks verwendet

Blocks, die auf Checkbox aufbauen.