Forms
Checkbox
A labeled checkbox on the Field contract, with the label to its right: pass label, and checked with onCheckedChange (or defaultChecked). Clicking the label toggles it, Space toggles it from the keyboard, the box fills and the tick draws, and indeterminate shows a dash announced as "mixed". With name, a checked box submits name=value with its form.
In Spoonjoy
Spoonjoy, a recipe app, is built with Ouro HX (how).
In its source: app/components/recipe/RecipePhotoStudio.tsx · app/components/recipe/SpoonDialog.tsx · app/routes/account.settings.tsx
Install
Once per app, with Tailwind CSS v4 and an @/* path alias (Ouro HX is built on Base UI, so --base base; skip -t in an existing app):
npx shadcn@latest init --base base --preset nova --yes --no-monoreponpx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.13Then the component. From v0.8.0 it installs only the helpers it imports and never touches your stylesheet (Installing):
npx shadcn@latest add ourostack/ouro-ui/checkbox#v1.2.13Or, with the @ouro namespace set up:
npx shadcn@latest add @ouro/ouro-base @ouro/checkboxIts tests, stories and Playwright spec are optional. They need Vitest 4.1.11 or later, and from v0.8.0 never change a version your app has (Testing):
npx shadcn@latest add ourostack/ouro-ui/checkbox#v1.2.13 ourostack/ouro-ui/checkbox-tests#v1.2.13import { Checkbox } from "@/components/ouro/checkbox/checkbox";Paths use shadcn's default @/ alias; the CLI rewrites them to yours.
Example
<Checkbox label="Email me new recipes" name="newsletter" defaultChecked />
<Checkbox label="I accept the terms" name="terms" required error={errors?.terms} />Use instead when
- Use
CheckboxGroupinstead for several choices under one legend that submit together,Switchfor a setting that applies at once, andRadioGroupwhen only one choice may be picked.
Props
Type: CheckboxProps.
checked- Type:
boolean - Whether it is checked, for a controlled checkbox; pair it with
onCheckedChange. className- Type:
string - Layout only: margins, width, grid placement (
mt-4,col-span-2). Appearance classes warn in development; style checkboxes in your theme. defaultChecked- Type:
boolean - Whether it starts checked, for an uncontrolled checkbox.
description- Type:
ReactNode - Supporting text under the label, read after it through
aria-describedby. disabled- Type:
boolean - Disables the checkbox and fades it.
error- Type:
ReadonlyErrorMessages - What is wrong, from your server action or client check, shown under the label; the box gets
aria-invalid. A message is text or a React node (one with a link); an empty string or array means valid. In aCheckboxGroup, pass it to the group. hideLabel- Type:
boolean - Hides the label visually; screen readers still read it as the checkbox's name.
indeterminate- Type:
boolean - Shows a dash and announces "mixed": neither checked nor unchecked, as a "select all" box does when some of its items are checked. It is controlled: a click does not clear it, so clear it in
onCheckedChange, as in<Checkbox indeterminate={some} checked={all} onCheckedChange={toggleAll}>. The box submits only whilechecked. label(required)- Type:
string - What checking it means, shown to the right of the box and read as its name. Required: every control needs a name. To hide it visually, keep it and add
hideLabel. name- Type:
string - The form field name it submits under when checked. In a
CheckboxGroup, the group'snameapplies. onCheckedChange- Type:
((checked: boolean) => void) - Called with the new state when it is checked or unchecked.
readOnly- Type:
boolean - Shows the state but ignores clicks and keys, unlike
disabled, which also fades it. ref- Type:
Ref<HTMLElement> - The box element, a
<span role="checkbox">. required- Type:
boolean - The box must be checked to submit the form (accepting terms): it gets
requiredandaria-required, and the label a marker. In aCheckboxGroup, pass it to the group. value- Type:
string - What a checked box submits (
onwithout one). In aCheckboxGroup, it identifies the box in the group'svalue, so it is required there.
Don't use
These are not props. Passing one fails the type check with the fix in the error, and in development a console message names the fix.
defaultValue: Not a prop. A checkbox's starting state isdefaultChecked:<Checkbox defaultChecked>.errorMessage: Not a prop. Ouro UI useserror:<Checkbox error="Required.">.helperText: Not a prop. Ouro UI usesdescription:<Checkbox description="...">.invalid: Not a prop.errormarks the checkbox invalid:<Checkbox error="What is wrong">.isChecked: Not a prop. Ouro UI useschecked:<Checkbox checked={agreed}>.isDisabled: Not a prop. Ouro UI usesdisabled:<Checkbox disabled>.isIndeterminate: Not a prop. Ouro UI usesindeterminate:<Checkbox indeterminate>.isInvalid: Not a prop.errormarks the checkbox invalid:<Checkbox error="What is wrong">.isRequired: Not a prop. Ouro UI usesrequired:<Checkbox required>.onChange: Not a prop. Ouro UI usesonCheckedChange, which gets the new state:onCheckedChange={setChecked}.
className is for layout only
Pass margins, width and grid placement (mt-4, w-full, col-span-2).
Appearance comes from props, not classes. In development, a class that sets appearance logs one warning per component instance, naming the prop to use instead. A class sets appearance when, after its variants (hover:, sm:) and any ! are removed, it starts with bg-, text-, border-, rounded-, shadow-, font-, ring-, from-, via-, to-, fill- or stroke-, or is border, rounded, shadow or ring, or has a dark: variant. Text wrapping, overflow and table layout classes (text-wrap, text-ellipsis, border-collapse) are layout and do not warn.
Alignment classes (text-center) warn too: set alignment on a parent element.
Types
ErrorMessage
One error message: text, or a React element for a message that carries a link or other markup.
type ErrorMessage = string | ReactElement;ReadonlyErrorMessages
A read-only message view accepted by fields and Form; mutable input arrays remain valid.
type ReadonlyErrorMessages = ErrorMessage | readonly ErrorMessage[];