Forms

CheckboxGroup

A group of checkboxes under one legend, on the Field contract, for picking any number of choices: pass legend, and options (or Checkbox children). value is the list of checked values; with name, each checked box submits name=value with its form. error and required apply to the whole group.

Herbs

Pick any you have fresh.

Sweet and peppery.

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-monorepo
npx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.13

Then 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-group#v1.2.13

Or, with the @ouro namespace set up:

npx shadcn@latest add @ouro/ouro-base @ouro/checkbox-group

Its 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-group#v1.2.13 ourostack/ouro-ui/checkbox-group-tests#v1.2.13
import { CheckboxGroup } from "@/components/ouro/checkbox/checkbox-group";

Paths use shadcn's default @/ alias; the CLI rewrites them to yours.

Example

<CheckboxGroup
  legend="Herbs"
  name="herbs"
  defaultValue={["basil"]}
  options={[
    { value: "basil", label: "Basil", description: "Sweet and peppery." },
    { value: "mint", label: "Mint" },
  ]}
/>

Use instead when

  • Use RadioGroup instead when exactly one choice may be picked, Checkbox for a single yes-or-no choice, and Fieldset to group different fields.

Props

The props the modes share. Type: CheckboxGroupProps.

className
Type: string
Layout only: margins, width, grid placement (mt-4, col-span-2). Appearance classes warn in development; style checkboxes in your theme.
defaultValue
Type: string[]
The values checked at first, for an uncontrolled group.
description
Type: ReactNode
Supporting text under the legend, read after it through aria-describedby.
disabled
Type: boolean
Disables every box and fades the group.
error
Type: ReadonlyErrorMessages
What is wrong, from your server action or client check, shown under the choices; every box gets aria-invalid. A message is text or a React node (one with a link); an empty string or array means valid.
hideLegend
Type: boolean
Hides the legend visually; screen readers still read it as the group's name.
legend (required)
Type: string
What the group is, shown above the choices and read as the group's name. Required: a group of controls needs a name. To hide it visually, keep it and add hideLegend.
name
Type: string
The form field name every checked box submits under, once per checked value.
onValueChange
Type: ((value: string[]) => void)
Called with every checked value, in the order they were checked, when one is checked or unchecked.
ref
Type: Ref<HTMLDivElement>
The group element, a <div role="group">.
required
Type: boolean
At least one box must be checked to submit the form: the legend gets a marker (and requiredLabel for screen readers), and the browser shows requiredMessage until one is checked. HTML has no native "at least one" rule for checkboxes, so this check runs only once the page has hydrated: your server action must still check it.
requiredLabel
Type: string
Default: "(required)"
What screen readers hear after the legend when required, since no box can carry aria-required; the asterisk is what shows. Translate it here.
requiredMessage
Type: string
Default: "Choose at least one."
What the browser says when required and nothing is checked. Translate it here.
value
Type: string[]
The checked values, for a controlled group; pair it with onValueChange.

Modes

CheckboxGroupProps is a union: pass the props of exactly one mode. Mixing them is a type error.

With children

children (required)
Type: ReactNode
The choices as Checkbox elements, each with a value, for a layout options cannot express.

Not in this mode (typed never):

  • options: Not with children: pass the choices one way.

With options

options (required)
Type: CheckboxGroupOption[]
The choices, one box each, in order.

Not in this mode (typed never):

  • children: Not with options: pass the choices one way.

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.

  • errorMessage: Not a prop. Ouro UI uses error: <CheckboxGroup error="Required.">.
  • helperText: Not a prop. Ouro UI uses description: <CheckboxGroup description="...">.
  • invalid: Not a prop. error marks the group invalid: <CheckboxGroup error="What is wrong">.
  • isDisabled: Not a prop. Ouro UI uses disabled: <CheckboxGroup disabled>.
  • isInvalid: Not a prop. error marks the group invalid: <CheckboxGroup error="What is wrong">.
  • isRequired: Not a prop. Ouro UI uses required: <CheckboxGroup required>.
  • items: Not a prop. Ouro UI uses options: options={[{ value: "basil", label: "Basil" }]}.
  • label: Not a prop. A group is named by its legend: <CheckboxGroup legend="Herbs">.
  • onChange: Not a prop. Ouro UI uses onValueChange, which gets the checked values: onValueChange={setValues}.

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

CheckboxGroupOption

One choice in a CheckboxGroup's options.

interface CheckboxGroupOption {
  /** What the box submits, and its entry in the group's `value`. */
  value: string;
  /** The choice's name, shown to the right of its box. */
  label: string;
  /** Supporting text under the label, read after it. */
  description?: ReactNode | undefined;
  /** Disables this one box. */
  disabled?: boolean | undefined;
}

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[];