Skip to content
Nizivo UI

Checkbox

A native checkbox with its label, including the indeterminate (mixed) state. It submits with the form before hydration; inside a Field it is labelled, described and validated by the Field.

Tier: client

Import

import { Checkbox } from '@nizivo/react/checkbox';

// Once per app (Getting started), after
// @nizivo/tokens/tokens.css and @nizivo/react/css/base.css
import '@nizivo/react/css/checkbox.css';

Static or interactive

Client. It always ships its client leaf, which wires it to a Field and sets indeterminate (a DOM property with no attribute), and inside a Field also FieldScope. The server HTML is a native checkbox, so it toggles and submits before hydration. How to choose.

Examples

Basic

A native checkbox: it submits value (default "on") under name, even before hydration.

import type { JSX } from 'react';
import { Checkbox } from '@nizivo/react/checkbox';

export function CheckboxExample(): JSX.Element {
  return (
    <Checkbox name="track-inventory" defaultChecked>
      Track inventory
    </Checkbox>
  );
}

States

Unchecked, checked, mixed, disabled and invalid. A mixed box submits as unchecked.

import type { JSX } from 'react';
import { Stack } from '@nizivo/react/stack';
import { Checkbox } from '@nizivo/react/checkbox';

export function CheckboxStatesExample(): JSX.Element {
  return (
    <Stack gap="3">
      <Checkbox name="states" value="unchecked">
        Unchecked
      </Checkbox>
      <Checkbox name="states" value="checked" defaultChecked>
        Checked
      </Checkbox>
      <Checkbox name="states" value="mixed" defaultChecked="indeterminate">
        Some variants selected
      </Checkbox>
      <Checkbox name="states" value="disabled" disabled>
        Disabled
      </Checkbox>
      <Checkbox name="states" value="disabled-checked" disabled defaultChecked>
        Disabled and checked
      </Checkbox>
      <Checkbox name="states" value="invalid" invalid>
        Invalid
      </Checkbox>
    </Stack>
  );
}

Group

A group of checkboxes is a Fieldset: the legend names the group.

Notify me about
Emails go to the store owner.
import type { JSX } from 'react';
import { Fieldset } from '@nizivo/react/fieldset';
import { Checkbox } from '@nizivo/react/checkbox';

export function CheckboxGroupExample(): JSX.Element {
  return (
    <Fieldset legend="Notify me about" description="Emails go to the store owner.">
      <Checkbox name="notify" value="orders" defaultChecked>
        New orders
      </Checkbox>
      <Checkbox name="notify" value="stock">
        Low stock
      </Checkbox>
      <Checkbox name="notify" value="payouts" disabled>
        Payouts (needs a payment provider)
      </Checkbox>
    </Fieldset>
  );
}

Field

Inside a Field, the Field label asks the question and the checkbox label answers it; the Field's description and error are joined to the box's aria-describedby.

Shown to buyers at checkout.
Accept the terms to publish.
import type { JSX } from 'react';
import { Field } from '@nizivo/react/field';
import { Checkbox } from '@nizivo/react/checkbox';

export function CheckboxFieldExample(): JSX.Element {
  return (
    <Field
      label="Terms of sale"
      description="Shown to buyers at checkout."
      error="Accept the terms to publish."
      required
    >
      <Checkbox name="terms">I accept the terms</Checkbox>
    </Field>
  );
}

Props

className and style go on the root <label>; every other attribute, ref included, goes on the native <input>, so form libraries (register, setFocus) reach the real control.

Client component Renders data-slot="checkbox" with the class nz-checkbox. Props: CheckboxProps. Source: src/checkbox/checkbox.client.tsx.

NameTypeDefaultDescription
checkedCheckboxCheckedState–

Controlled state. 'indeterminate' shows the dash; a click then reports true.

childrenReactNode–

The label, after the box. Without one, name the box with aria-label.

defaultCheckedCheckboxCheckedStatefalse

Uncontrolled initial state.

indicatorReactNode–

Replaces the internal check and dash glyphs (decorative), shown while checked or mixed.

invalidboolean–

An error message is presented for this box. Inside a Field, the Field decides.

onCheckedChange(checked: boolean, details: ChangeDetails<CheckboxChangeReason>) => void–

Called after the consumer's own onChange, unless it called preventDefault().

renderRenderProp<CheckboxState>–

Composition of the root <label>, the hit target.

  • Also takes every attribute of <input> except checked, children, defaultChecked, readOnly, size, type. (Omit<ComponentPropsWithRef<"input">, "type" | "checked" | "defaultChecked" | "children" | "readOnly" | "size">)

render takes an element, or, in client code, a function of the props and this state (CheckboxState):

NameTypeDefaultDescription
disabledrequiredboolean–
invalidrequiredboolean–

An error message is presented (the Field's, or the invalid prop).

requiredrequiredboolean–

Other exports

CheckboxChangeReason type

import type { CheckboxChangeReason } from '@nizivo/react/checkbox'

Every change comes from the native change event: a click, Space, or a click on the label.

type CheckboxChangeReason = "input"

CheckboxCheckedState type

import type { CheckboxCheckedState } from '@nizivo/react/checkbox'

'indeterminate' is the mixed state of a "select all" box; it submits as unchecked.

type CheckboxCheckedState = boolean | "indeterminate"

CheckboxProps type

import type { CheckboxProps } from '@nizivo/react/checkbox'

The props of Checkbox, listed above.

CheckboxState type

import type { CheckboxState } from '@nizivo/react/checkbox'

What a render function receives for the root <label>.

The render state of Checkbox, listed above.

Data attributes and slots

Style parts by class (.nz-<slot>) and state by data attribute. Attribute meanings come from contracts/vocabulary.json.

SlotRendered byData attributes
checkbox
.nz-checkbox
Checkbox
root (takes render)
  • data-disabled (present or absent) ← disabled
    Mirrors the disabled attribute
  • data-invalid (present or absent) ← invalid
    An error message is currently presented (not "fails constraints"); always the Field's own state, never the engine's
  • data-required (present or absent) ← required
    Mirrors the required attribute
checkbox-indicator
.nz-checkbox-indicator
Checkbox
inner element (CSS only)
none
checkbox-input
.nz-checkbox-input
Checkbox
inner element (CSS only); interactive, focus: self; forced colours: border, fill on :checked, fill on :indeterminate
none
checkbox-label
.nz-checkbox-label
Checkbox
inner element (CSS only)
none

CSS hooks

Set a hook on the component's class or any ancestor to restyle it without specificity games; leave it unset for the default shown.

HookDefaultRead as
--nz-checkbox-accent--nz-bg-brand
  • --_nz-checkbox-bg on .nz-checkbox-input:is(:checked, :indeterminate)
--nz-checkbox-gap--nz-space-2
  • column-gap on .nz-checkbox
--nz-checkbox-radius--nz-radius-xs
  • border-radius on .nz-checkbox-input
--nz-checkbox-size
  • default: 1.25rem
  • block-size on .nz-checkbox-indicator
  • inline-size on .nz-checkbox-indicator
  • margin-block-start on .nz-checkbox-indicator
  • block-size on .nz-checkbox-input
  • inline-size on .nz-checkbox-input
  • margin-block-start on .nz-checkbox-input

Layers: nz.components. Classes: .nz-checkbox, .nz-checkbox-indicator, .nz-checkbox-input, .nz-checkbox-label, .nz-glyph-check, .nz-glyph-minus.

Tokens this stylesheet reads (17)

--nz-bg-brand, --nz-bg-disabled, --nz-bg-muted, --nz-bg-surface, --nz-border-control, --nz-border-control-hover, --nz-border-critical, --nz-border-subtle, --nz-border-width-default, --nz-duration-fast, --nz-easing-standard, --nz-font-body-m, --nz-radius-xs, --nz-space-2, --nz-text-disabled, --nz-text-on-brand, --nz-text-primary

Accessibility

These parts are interactive. Each gets the focus outline and touch-action rule from @nizivo/react/css/base.css (the nz.base layer), so the floor survives a restyle:

  • checkbox-input: draws the focus outline itself on keyboard focus.

In forced-colours mode these states keep a visible system colour: checkbox-input (border), checkbox-input:checked (fill), checkbox-input:indeterminate (fill).

Pattern: WAI-ARIA Authoring Practices.

Keyboard

KeysAction
TabMoves focus to the checkbox.
SpaceToggles it. A mixed checkbox becomes checked.

Source: a native <input type="checkbox"> (checkbox.client.tsx), which adds no key handling; the browser lane (test/browser/toggles.spec.tsx) checks the mixed state and form data.

Figma

Node ids from design/component-map.json, which nz gen writes from component.json. The links open the Nizivo Figma library, which may ask you to sign in.

On this page