Checkbox
Selects one or more options whose values are confirmed on submit. Built on Base
UI Checkbox, with checked, unchecked, and indeterminate states. For an
on/off setting that takes effect immediately, use Switch instead.
Import
import {
Checkbox,
CheckboxGroup,
CheckboxCard,
} from "@photon-ai/pho-ui/components/checkbox";
States
Pass label for a clickable text row. indeterminate renders a dash — use it
for a parent whose children are partially selected.
<Checkbox label="Unchecked" />
<Checkbox label="Checked" defaultChecked />
<Checkbox label="Indeterminate" indeterminate />
<Checkbox label="Disabled" disabled />
Sizes
Two sizes — sm and md (default). size scales the box and its glyph; the
label keeps the body text size.
<Checkbox size="sm" label="Small" defaultChecked />
<Checkbox size="md" label="Medium" defaultChecked />
Group
CheckboxGroup tracks several checkboxes as one value. Each child sets a
name; control the group with value / onValueChange or defaultValue.
<CheckboxGroup defaultValue={["email"]}>
<Checkbox name="email" label="Email" />
<Checkbox name="sms" label="SMS" />
<Checkbox name="push" label="Push notifications" />
</CheckboxGroup>
Card
CheckboxCard wraps the same Checkbox in a bordered, selectable tile — pass
title and an optional description instead of label. Use it when each
option carries enough weight (a plan add-on, a notification channel) to
deserve more visual presence than a plain labeled box. The whole tile is the
hit target and carries the press feedback.
<CheckboxCard
title="Email"
description="Order updates and receipts."
defaultChecked
/>
<CheckboxCard title="SMS" description="Delivery and shipping alerts." />
Card with icon
Pass icon when the option has a glyph — it stacks above the title and
replaces the checkbox box.
<CheckboxCard
title="Email"
description="Order updates and receipts."
icon={<IconMail />}
defaultChecked
/>
<CheckboxCard
title="SMS"
description="Delivery and shipping alerts."
icon={<IconMessage />}
/>
Props
Checkbox (<span role="checkbox"> + a hidden native input)
- label —
ReactNode— clickable text beside the box; the whole row toggles it (omit and passaria-labelfor a bare box) - size —
sm·md(defaultmd) — box and glyph size - checked —
boolean— controlled checked state - defaultChecked —
boolean(defaultfalse) — initial state when uncontrolled - indeterminate —
boolean(defaultfalse) — render the partial (dash) state; announces asmixed - onCheckedChange —
(checked: boolean, details) => void— fires on toggle - name —
string— value key contributed to a parentCheckboxGroup - value —
string(default'on') — form-submission value when checked - parent —
boolean(defaultfalse) — mark this the “select all” summary box of aCheckboxGroup - disabled —
boolean(defaultfalse) — non-interactive and dimmed - readOnly —
boolean(defaultfalse) — focusable but not toggleable - required —
boolean(defaultfalse) — must be checked to submit a form - className —
string— merged onto the box
CheckboxGroup (<div>)
- value —
string[]— controlled set of checked childnames - defaultValue —
string[]— initial set when uncontrolled - onValueChange —
(value: string[], details) => void— fires when the set changes - allValues —
string[]— every child value, so aparentbox can drive select-all - disabled —
boolean(defaultfalse) — disable every child at once - className —
string
CheckboxCard
- title (required) — tile heading
- description — supporting copy shown below the title
- icon —
ReactNode— glyph stacked above the title; when set, it replaces the checkbox box - checked / defaultChecked / onCheckedChange — same as
Checkbox - indeterminate —
boolean(defaultfalse) — render the partial (dash) state - disabled —
boolean(defaultfalse) — non-interactive and dimmed - className —
string— merged onto the tile
Accessibility
- Semantics — the box renders as a
<span role="checkbox">witharia-checkedset totrue,false, ormixedforindeterminate; a hidden native input carries the value into forms. - Labelling — passing
labelwraps the box and text in a<label>, so the text names the control and clicking anywhere on the row toggles it. Without alabel, passaria-label— a bare box is announced as an unnamed checkbox.CheckboxCardworks the same way —titleanddescriptionsit inside the tile’s<label>, so they compose into the option’s accessible name. - Keyboard —
Spacetoggles the focused checkbox;Tab/Shift+Tabmove between them.disableddrops it from the tab order;readOnlykeeps it focusable but ignores the toggle. - Focus — keyboard focus shows the brand ring (
focus-visibleonly, offset from the box); pointer clicks don’t. - Groups — inside a
CheckboxGroup, aparentbox given the group’sallValuesreportsmixedwhen only some children are checked. Give the set its own accessible name — a heading tied witharia-labelledby, or aFieldset— so the group reads as a unit.
Best practices
- Use a checkbox for values confirmed on submit (filters, terms, multi-select);
use a
Switchfor a setting that applies the moment it flips. - Give every checkbox a label or
aria-label— a lone box is ambiguous. - Reserve
indeterminatefor a “select all” parent reflecting a partial child selection; it isn’t a third user-settable value.