Skip to main content

Checkboxes

Overview​

The Checkboxes component wraps multiple Checkbox components in a Bulma-styled group.

Use for lists of boolean choices, such as preference lists or to-do checklists.


Import​

import { Checkboxes, Checkbox } from '@allxsmith/bestax-bulma';

Usage​

Grouped Checkboxes​

This example demonstrates the Checkboxes component wrapping multiple Checkbox children. Use this pattern for lists of boolean options, such as to-do lists or preference selections. Each Checkbox receives its own label via the children prop.

<Checkboxes>
  <Checkbox> Make the bed </Checkbox>
  <Checkbox> Brush teeth </Checkbox>
  <Checkbox> Do homework </Checkbox>
  <Checkbox> Feed the pet </Checkbox>
  <Checkbox> Take out the trash </Checkbox>
  <Checkbox> Clean your room </Checkbox>
  <Checkbox> Set the table </Checkbox>
  <Checkbox> Help with dishes </Checkbox>
  <Checkbox> Water the plants </Checkbox>
  <Checkbox> Put away toys </Checkbox>
</Checkboxes>


Context-Aware Rendering​

The Checkboxes component is context-aware: it detects whether it is already inside a Field or Control and adjusts its rendering accordingly. This means you can use it standalone with a label prop (it wraps itself in Field+Control), inside a Field (it skips its own Field), or inside both Field and Control (it renders only the raw checkbox group).

Default (with label)​

The simplest usage — the component automatically renders its own Field and Control wrappers.

<Checkboxes label="Chores">
  <Checkbox> Make the bed </Checkbox>
  <Checkbox> Brush teeth </Checkbox>
  <Checkbox> Do homework </Checkbox>
</Checkboxes>


With Field Wrapper​

When you need manual control over the Field layout (e.g., horizontal forms), wrap the component in Field. The component detects it's inside a Field and skips rendering its own.

A labeled Field that holds the Checkboxes directly names the group through aria-labelledby, with no extra wiring. In a horizontal form the label sits on the outer Field and the Checkboxes in an inner one, which starts its own scope, so these examples point the group at the label by hand: labelProps={{ id, htmlFor: undefined }} on that Field and a matching aria-labelledby on the Checkboxes. The htmlFor: undefined keeps the label from pointing a for at a control, since a group takes none.

function example() {
  return (
    <Field
      horizontal
      label="Chores"
      labelProps={{ id: 'chores-label', htmlFor: undefined }}
    >
      <Field.Body>
        <Field>
          <Checkboxes aria-labelledby="chores-label">
            <Checkbox> Make the bed </Checkbox>
            <Checkbox> Brush teeth </Checkbox>
            <Checkbox> Do homework </Checkbox>
          </Checkboxes>
        </Field>
      </Field.Body>
    </Field>
  );
}


With Field and Control Wrappers​

For full manual control, wrap in both Field and Control. The component detects both and renders only the raw checkbox group.

function example() {
  return (
    <Field
      horizontal
      label="Chores"
      labelProps={{ id: 'chores-label-control', htmlFor: undefined }}
    >
      <Field.Body>
        <Field>
          <Control>
            <Checkboxes aria-labelledby="chores-label-control">
              <Checkbox> Make the bed </Checkbox>
              <Checkbox> Brush teeth </Checkbox>
              <Checkbox> Do homework </Checkbox>
            </Checkboxes>
          </Control>
        </Field>
      </Field.Body>
    </Field>
  );
}

Inside a Control with no Field around it, Checkboxes renders no Field of its own either, unless you give it label, message, horizontal or fieldClassName. Those need a Field, so with any of them it keeps its own Field inside the Control and warns in development. Wrap the Control in a Field, as above, and set the label, horizontal and class name on that Field instead.


Compound (dot-notation) usage​

Checkbox is also available as Checkboxes.Checkbox, so a checkbox group can be composed from the single Checkboxes import.

<Checkboxes name="frameworks" label="Frameworks">
  <Checkboxes.Checkbox value="react">React</Checkboxes.Checkbox>
  <Checkboxes.Checkbox value="vue">Vue</Checkboxes.Checkbox>
</Checkboxes>


Group State​

Checkboxes can manage the selected-values array for the entire group, matching the pattern used by React Aria's CheckboxGroup. Three usage modes:

  1. Name-only — pass just name. Each child Checkbox manages its own checked state. Backwards compatible.
  2. Controlled — pass value (an array of selected values) and onChange. The parent owns selection; each Checkbox derives checked from value.includes(my.value).
  3. Uncontrolled — pass defaultValue (an array) and optionally onChange. The group manages internal state; onChange fires with the new array.

Local props (checked, onChange) on individual Checkbox children always win over the group context.

Controlled​

function ControlledCheckboxes() {
  const [tags, setTags] = React.useState(['react']);
  return (
    <div>
      <Checkboxes name="tags" value={tags} onChange={setTags}>
        <Checkbox value="react">React</Checkbox>
        <Checkbox value="vue">Vue</Checkbox>
        <Checkbox value="angular">Angular</Checkbox>
        <Checkbox value="svelte">Svelte</Checkbox>
      </Checkboxes>
      <p style={{ marginTop: '1rem' }}>
        Selected: <strong>{tags.length ? tags.join(', ') : '(none)'}</strong>
      </p>
    </div>
  );
}

Uncontrolled​

function UncontrolledCheckboxes() {
  const [latest, setLatest] = React.useState<string[]>([]);
  return (
    <div>
      <Checkboxes
        name="features"
        defaultValue={['darkmode']}
        onChange={setLatest}
      >
        <Checkbox value="darkmode">Dark mode</Checkbox>
        <Checkbox value="notifications">Notifications</Checkbox>
        <Checkbox value="analytics">Analytics</Checkbox>
      </Checkboxes>
      {latest.length > 0 && (
        <p style={{ marginTop: '1rem' }}>
          Latest: <strong>{latest.join(', ')}</strong>
        </p>
      )}
    </div>
  );
}


Form Submission​

Checkboxes is HTML-form-compatible. Pass a name prop on the group and every child Checkbox inherits it via React context (works at any nesting depth). Each checked box submits as name=value, producing a standard form-encoded array (e.g., tags=react&tags=vue) that server-side parsers handle natively.

PropDescription
nameForm field name shared by every child Checkbox. Children with their own name prop keep theirs (explicit > implicit).
function CheckboxesFormDemo() {
  const [submitted, setSubmitted] = React.useState('');
  return (
    <form
      onSubmit={e => {
        e.preventDefault();
        const fd = new FormData(e.currentTarget);
        setSubmitted(JSON.stringify(Array.from(fd.entries()), null, 2));
      }}
    >
      <Checkboxes name="tags">
        <Checkbox value="react" defaultChecked>
          React
        </Checkbox>
        <Checkbox value="vue" defaultChecked>
          Vue
        </Checkbox>
        <Checkbox value="angular">Angular</Checkbox>
        <Checkbox value="svelte">Svelte</Checkbox>
      </Checkboxes>
      <div style={{ marginTop: '1rem' }}>
        <button type="submit" className="button is-primary">
          Submit
        </button>
      </div>
      {submitted && <pre style={{ marginTop: '1rem' }}>{submitted}</pre>}
    </form>
  );
}


Accessibility​

  • The group is rendered as a <div class="checkboxes" role="group"> containing labeled checkboxes.
  • The label prop names the whole group: the rendered <label> gets an id (yours via labelProps.id, or a generated one) and the group points at it with aria-labelledby.
  • Inside a labeled Field, that Field's label names the group the same way.
  • An aria-label or aria-labelledby you give the group wins over either label, whether that label sits on the Checkboxes or on a Field around them.
  • Each child should be a Checkbox for proper labeling and accessibility.

  • Checkbox: Individual checkbox.
  • Field: For labeled/grouped form fields.

Additional Resources​


Props​

PropTypeDefaultDescription
labelReact.ReactNode—Field label naming the whole group. Automatically associated via aria-labelledby on the role="group" wrapper — uses your labelProps.id when provided, otherwise a generated one. Dropped inside an outer Field, whose own label names the group instead through aria-labelledby when that Field generates a target id (not grouped/hasAddons, no explicit labelProps.htmlFor). An aria-label or aria-labelledby you set on the group wins over either label, which still renders but no longer names it. An aria-labelledby key counts even when undefined, since it is spread over the group's own.
labelPropsReact.LabelHTMLAttributes<HTMLLabelElement> & { [key: string]: unknown; }—Props for the label element. An explicit id here is used as the aria-labelledby target instead of a generated one; any htmlFor is ignored (a group label names the group, never a single control).
classNamestring—Additional CSS classes to apply.
namestring—Form field name shared by every Checkbox in the group (via context).
valuestring[]—Currently-selected values (controlled mode).
defaultValuestring[]—Initial selected values (uncontrolled mode).
onChange(values: string[]) => void—Fires when the selection changes; receives the new array.
childrenReact.ReactNode—Checkbox elements to render in the group.
labelSize'small' | 'normal' | 'medium' | 'large'—Size for the label (used in horizontal layouts).
horizontalbooleanfalseHorizontal field layout.
messageReact.ReactNode—Help/validation message below the input.
messageColor'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger'—Bulma color for the message.
fieldClassNamestring—Additional CSS classes for the Field wrapper.
...Bulma helper props—See Helper Props

Subcomponents: