Gecko UIGecko UI

Checkbox

Customizable checkbox with support for indeterminate state

Checkbox

A checkbox component with custom styling and support for the indeterminate state, useful for "select all" scenarios where only some items are selected.

Installation

import { Checkbox } from '@geckoui/geckoui';

Basic Usage

import { useState } from 'react';

function Example() {
  const [checked, setChecked] = useState(false);

  return (
    <Checkbox
      checked={checked}
      onChange={(e) => setChecked(e.target.checked)}
    />
  );
}

Props API

PropTypeDefaultDescription
checkedboolean-Controlled checked state
indeterminatebooleanfalseShow the mixed state. Independent of checked, like the native DOM property
disabledbooleanfalseDisable the checkbox
aria-invalidboolean-Marks the box invalid, to a screen reader and in its colours
classNamestring-CSS class for the checkbox box
onChangeChangeEventHandler-Callback when checked state changes
...restInputHTMLAttributes-All standard HTML input attributes

Examples

With Label

import { useState } from 'react';

function CheckboxWithLabel() {
  const [agreed, setAgreed] = useState(false);

  return (
    <label className="flex items-center gap-2 cursor-pointer">
      <Checkbox
        checked={agreed}
        onChange={(e) => setAgreed(e.target.checked)}
      />
      <span>I agree to the terms and conditions</span>
    </label>
  );
}

Indeterminate State

import { useState } from 'react';

function SelectAll() {
  const [items, setItems] = useState([
    { id: 1, checked: false },
    { id: 2, checked: false },
    { id: 3, checked: false },
  ]);

  const allChecked = items.every(item => item.checked);
  const someChecked = items.some(item => item.checked);

  const handleSelectAll = (e: React.ChangeEvent<HTMLInputElement>) => {
    setItems(items.map(item => ({ ...item, checked: e.target.checked })));
  };

  return (
    <div>
      <label className="flex items-center gap-2">
        <Checkbox
          checked={allChecked}
          indeterminate={someChecked && !allChecked}
          onChange={handleSelectAll}
        />
        <span>Select All</span>
      </label>
      {/* Individual items... */}
    </div>
  );
}

How indeterminate behaves

indeterminate mirrors the native DOM property. It is purely visual and independent of checked, so a "select all" box shows the dash while staying unchecked:

checkedindeterminateShows
falsefalseempty box
falsetruedash
truefalsetick
truetruedash

Because it is a DOM property with no matching HTML attribute, the component writes it to the input directly. That is what makes the :indeterminate CSS pseudo-class match and lets assistive tech report the mixed state.

Disabled States

<div className="space-y-2">
  <Checkbox checked disabled />
  <Checkbox disabled />
</div>

Multiple Checkboxes

function NotificationSettings() {
  const [settings, setSettings] = useState({
    email: true,
    sms: false,
    push: true,
  });

  return (
    <div className="space-y-3">
      <label className="flex items-center gap-2">
        <Checkbox
          checked={settings.email}
          onChange={(e) => setSettings({...settings, email: e.target.checked})}
        />
        <span>Email notifications</span>
      </label>
      {/* Other checkboxes... */}
    </div>
  );
}

Keyboard Navigation

The checkbox supports full keyboard navigation:

  • Space or Enter: Toggle checkbox
  • Tab: Move focus to next element
  • Shift + Tab: Move focus to previous element

Accessibility

  • Renders a native <input type="checkbox">, so role and checked state are implicit
  • The indeterminate DOM property is set on the input, so the mixed state is announced
  • Keyboard accessible with Space and Enter keys
  • Focus management for keyboard users
  • Works with screen readers
  • Disabled state properly communicated

Styling

  • GeckoUICheckbox - Main container
  • GeckoUICheckbox__input - Native checkbox input, styled as the visual box
  • GeckoUICheckbox__icon - Check or indeterminate icon

Icons

  • CheckIcon: Displayed when checked={true} and indeterminate={false}
  • IndeterminateIcon: Displayed when indeterminate={true}
  • Radio - For mutually exclusive selections
  • Switch - Toggle alternative
  • RHFCheckbox - React Hook Form integration