Checkbox

checkbox() renders a <label> wrapping a real <input type="checkbox"> and the box you see. The input is visually hidden but still there, so it is focusable, it submits, and the whole label is a hit target — the tick mark is drawn from the input’s own :checked state, with no script involved.

Basic checkbox

stack({ direction: 'row', gap: 'lg' },
  checkbox({ label: 'Email me updates', name: 'updates' }),
  checkbox({ label: 'Checked', name: 'checked', checked: true }),
)

Colors

stack({ direction: 'row', gap: 'lg', wrap: true },
  checkbox({ label: 'Primary', checked: true, color: 'primary' }),
  checkbox({ label: 'Neutral', checked: true, color: 'neutral' }),
  checkbox({ label: 'Success', checked: true, color: 'success' }),
  checkbox({ label: 'Warning', checked: true, color: 'warning' }),
  checkbox({ label: 'Danger', checked: true, color: 'danger' }),
)

Disabled

stack({ direction: 'row', gap: 'lg' },
  checkbox({ label: 'Unavailable', disabled: true }),
  checkbox({ label: 'On, and locked', checked: true, disabled: true }),
)

Long labels

The box stays aligned with the first line rather than centring itself against a paragraph.

checkbox({
  label: 'Run a Lighthouse audit after every build, and fail the build when a score drops below its threshold.',
  name: 'lighthouse',
  checked: true,
})

Groups

choiceGroup() builds a set of checkboxes from data, with a shared legend and name. Pass an array as value to tick several.

Generate
Each one is written into dist/ at the end of the build.
choiceGroup({
  legend: 'Generate',
  name: 'generate',
  type: 'checkbox',
  value: ['sitemap', 'rss'],
  options: [
    { value: 'sitemap', label: 'sitemap.xml' },
    { value: 'rss', label: 'rss.xml' },
    { value: 'pagefind', label: 'Pagefind index' },
  ],
  help: 'Each one is written into dist/ at the end of the build.',
})

In a row

Categories
choiceGroup({
  legend: 'Categories',
  name: 'categories',
  type: 'checkbox',
  direction: 'row',
  value: ['performance'],
  options: ['performance', 'accessibility', 'seo'],
})

With a field

A single checkbox rarely needs a label above it as well. When a group does, field() gives it the same label, help and error treatment as a text field.

You need to accept the terms to continue.
field({ label: 'Terms', error: 'You need to accept the terms to continue.' },
  checkbox({ label: 'I accept the terms', name: 'terms', color: 'danger' }),
)

Props

PropTypeDefaultDescription
labelChildText beside the box. Omit it for a bare control.
color'primary' | 'neutral' | 'success' | 'warning' | 'danger''primary'Colour when checked.
checkedbooleanfalseWhether it starts ticked.
namestringForm field name.
valuestring | numberValue submitted when checked.
disabledbooleanfalseDisables the input and dims the label.

Anything else lands on the <input>, not the label — so required, onchange and data-* go where you would expect. Use class to style the label itself.

For a set built from data, see choiceGroup() on the Radio group page — it takes the same options either way, switched by type: 'checkbox'.