Browse docs
Browse docs
A binary choice. Backed by Radix UI's checkbox primitive — keyboard navigation, ARIA attributes, and the indeterminate state come for free.
import { Checkbox } from '@dashforge/tw';
<Checkbox label="I agree to the terms" />Standalone:
const [agreed, setAgreed] = useState(false);
<Checkbox
label="I agree to the terms"
checked={agreed}
onCheckedChange={setAgreed}
/>Inside a form:
import { DashForm } from '@dashforge/forms';
import { Checkbox, Button } from '@dashforge/tw';
<DashForm onSubmit={subscribe}>
<Checkbox name="newsletter" label="Subscribe to the weekly digest" />
<Checkbox name="terms" label="I accept the terms of service" required />
<Button type="submit" color="primary">Subscribe</Button>
</DashForm>The bridge registers the boolean automatically — no Controller wrap.
import { Checkbox, Stack } from '@dashforge/tw';
<Stack gap={2}>
<Checkbox name="terms" label="I accept the terms and conditions" />
<Checkbox name="newsletter" label="Send me the monthly newsletter" defaultChecked />
</Stack><Checkbox
label="Send weekly digest"
helper="A Monday morning summary of activity in your workspace."
/>Helper text is rendered below the label in a muted color. Keep it short — one sentence — so the row stays scannable in a settings list.
{/* Uncontrolled — internal state */}
<Checkbox label="Auto-save" defaultChecked />
{/* Controlled — you own the state */}
<Checkbox label="Auto-save" checked={autoSave} onCheckedChange={setAutoSave} />onCheckedChange is the Radix-style callback — receives the next boolean directly (no synthetic event). Use onChange if you need the underlying input event.
const [checked, setChecked] = useState<boolean | 'indeterminate'>('indeterminate');
<Checkbox
label="Select all"
checked={checked}
onCheckedChange={setChecked}
/>Pass the literal string 'indeterminate' as the checked value (or set it via state) to render the dash glyph. Useful for "select all" patterns where some children are selected.
import { Checkbox, Stack } from '@dashforge/tw';
<Stack gap={2}>
<Checkbox name="state-unchecked" label="Unchecked" />
<Checkbox name="state-checked" label="Checked" defaultChecked />
<Checkbox name="state-disabled" label="Disabled" disabled />
<Checkbox name="state-disabled-checked" label="Disabled + checked" disabled defaultChecked />
</Stack><Checkbox label="Premium feature" disabled helper="Upgrade your plan to enable." />Disabled checkboxes are removed from tab order and rendered at reduced opacity. The label is also dimmed for visual coherence.
You must accept the terms to continue.
import { Checkbox } from '@dashforge/tw';
<Checkbox
name="terms-required"
label="I accept the terms and conditions"
error
helperText="You must accept the terms to continue."
/>When error is set, the checkbox renders with the danger ring and the helper text turns red — same convention as TextField.
<Checkbox
name="adminFlag"
label="Mark as VIP"
access={{ requires: 'workspace.admin', when: 'denied:hide' }}
/>Three when modes ('denied:hide' | 'denied:disable' | 'denied:readonly') — same semantics as Button and TextField.
The classic three-row settings panel:
<div className="space-y-2">
<Checkbox name="emailNotifs" label="Email notifications"
helper="Weekly activity digest." defaultChecked />
<Checkbox name="pushNotifs" label="Push notifications"
helper="Real-time alerts in your browser." />
<Checkbox name="smsNotifs" label="SMS notifications"
helper="Critical security alerts only." />
</div>Configure <Checkbox> defaults application-wide.
import { patchTheme } from '@dashforge/tw-theme';
patchTheme({
components: {
Checkbox: {
defaults: { size: 'md' },
},
},
});Configurable axes (CheckboxVariantProps):
| Axis | Type | Notes |
|---|---|---|
size | 'sm' | 'md' | 'lg' | Control box size + label font-size. |
Non-visual axes (name, rules, label, helperText, error, disabled, checked, defaultChecked, access, visibleWhen, onCheckedChange) are not theme-configurable.
Precedence chain (lowest → highest):
defaultVariants from the internal checkboxVariants recipe.theme.components.Checkbox.defaults (application-wide).<Checkbox size="lg" />) — wins over theme.sx — appended to the root and wins over conflicting classes via tailwind-merge.TypeScript: theme.components.Checkbox.defaults autocompletes to size only; unrecognized fields are compile-time errors.
Reactivity: useComponentDefaults('Checkbox') subscribes to the theme store — patchTheme re-renders every mounted instance inheriting the changed axis.
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | — | Bridge field name (required when used inside DashFormProvider). |
access | AccessRequirement | — | RBAC requirement. |
checked | boolean | — | Initial / controlled checked state. |
defaultChecked | boolean | — | Default checked when used uncontrolled. |
disabled | boolean | — | Disabled — combined with RBAC disabled via OR. |
error | boolean | — | Explicit error flag. When true, the control renders with the danger ring + the error-text slot is used for helperText. Overrides the bridge's auto-detected error. |
helperText | ReactNode | — | Forces helper text below the control. When the bridge surfaces an error and helperText is undefined, the bridge's error message shows up here (gated by touched || submitCount > 0). |
label | ReactNode | — | Visible label rendered next to the control. Click also toggles the box. |
onCheckedChange | (checked: boolean) => void | — | Called when the user toggles the control. When inside a DashFormProvider, the bridge update happens first, then this callback fires with the new checked state. |
rules | unknown | — | Forwarded to the bridge as RHF rules — opaque to this component. |
size | 'md' | 'sm' | 'lg' | 'md' | Density tier — drives control box + label font-size. |
slotProps | CheckboxSlotProps | — | Per-slot overrides — see CheckboxSlotProps. |
sx | string | — | Root-level Tailwind override. Wins over variant classes via cn(). |
visibleWhen | (engine: Engine) => boolean | — | Engine predicate. When provided, the component renders only when the predicate returns truthy against the current engine state. |
<Checkbox> is a compound component with 6 named slots. Each slot accepts a { className?: string } override via slotProps:
| Slot | Purpose |
|---|---|
root | Outer <label> wrapping the whole row (control + label + helper). |
control | The visible Radix checkbox <button role="checkbox">. |
indicator | The check/indeterminate glyph inside the control. |
label | The text label next to the control. |
helperText | Helper text line, when not in error state. |
errorText | Helper text line, when in error state. |
Use sx for a root-level class override; use slotProps when you need to reach a specific inner element (e.g. control border color, errorText font style).
@radix-ui/react-checkbox) — the accessibility primitives are not hand-rolled. Indeterminate state, focus management, and keyboard handling are Radix's responsibility, not ours.<input type="checkbox"> — it's a styled <button role="checkbox">. A real hidden <input> is present for form serialization compatibility (browsers POST the value when a <form> is submitted natively).<input type="hidden"> if you need to persist the indeterminate semantics.