Open UI

Checkbox

A checkbox allows a user to select multiple items from a list of individual items, or to mark one individual item as selected.

import { Checkbox } from "@opengovsg/oui"export const Example = () => {  return (    <Checkbox defaultSelected value="option">      Option    </Checkbox>  )}

Usage

import { Checkbox, CheckboxGroup } from "@opengovsg/oui"
<Checkbox>Accept terms and conditions</Checkbox>

Alternatively, install the component as local source via the shadcn CLI:

npx shadcn@latest add https://oui.open.gov.sg/r/checkbox.json
pnpm dlx shadcn@latest add https://oui.open.gov.sg/r/checkbox.json
npx shadcn@latest add https://oui.open.gov.sg/r/checkbox.json
bunx --bun shadcn@latest add https://oui.open.gov.sg/r/checkbox.json

Checkbox is built on React Aria's Checkbox. It handles focus management, keyboard activation, and ARIA semantics automatically. Use CheckboxGroup to group multiple related checkboxes with a shared label, description, and validation.

If a checkbox does not have a visible label, pass aria-label or aria-labelledby to identify it to assistive technology.

Examples

Disabled

import { Checkbox } from "@opengovsg/oui"export const Example = () => {  return (    <div className="flex gap-4">      <Checkbox isDisabled>Option</Checkbox>      <Checkbox defaultSelected isDisabled>        Option      </Checkbox>    </div>  )}

Sizes

import { Checkbox } from "@opengovsg/oui"export const Example = () => {  return (    <div className="flex gap-4">      <Checkbox defaultSelected size="xs">        Extra Small (xs)      </Checkbox>      <Checkbox defaultSelected size="sm">        Small (sm)      </Checkbox>      <Checkbox defaultSelected size="md">        Medium (md)      </Checkbox>    </div>  )}

Colours

import { Checkbox } from "@opengovsg/oui"export const Example = () => {  return (    <div className="flex gap-4">      <Checkbox defaultSelected color="default">        Default      </Checkbox>    </div>  )}

Indeterminate

The isIndeterminate prop sets a Checkbox to an indeterminate state, overriding its appearance and maintaining it until set to false, regardless of user interaction.

import { Checkbox } from "@opengovsg/oui"export const Example = () => {  return (    <Checkbox isIndeterminate value="option">      Option    </Checkbox>  )}

Checkbox Group

Use CheckboxGroup to group related checkboxes with a shared label.

import { Checkbox, CheckboxGroup } from "@opengovsg/oui"export const Example = () => {  return (    <CheckboxGroup      defaultValue={["buenos-aires", "london"]}      label="Select cities"    >      <Checkbox value="buenos-aires">Buenos Aires</Checkbox>      <Checkbox value="sydney">Sydney</Checkbox>      <Checkbox value="san-francisco">San Francisco</Checkbox>      <Checkbox value="london">London</Checkbox>      <Checkbox value="tokyo">Tokyo</Checkbox>    </CheckboxGroup>  )}

Group Disabled

import { Checkbox, CheckboxGroup } from "@opengovsg/oui"export const Example = () => {  return (    <CheckboxGroup      isDisabled      defaultValue={["buenos-aires", "london"]}      label="Select cities"    >      <Checkbox value="buenos-aires">Buenos Aires</Checkbox>      <Checkbox value="sydney">Sydney</Checkbox>      <Checkbox value="san-francisco">San Francisco</Checkbox>      <Checkbox value="london">London</Checkbox>      <Checkbox value="tokyo">Tokyo</Checkbox>    </CheckboxGroup>  )}

Validation

See the Forms guide for end-to-end patterns including React Hook Form + Zod.

Validation is applied at the CheckboxGroup level:

  • isRequired — marks the group as required; at least one checkbox must be selected.
  • isInvalid + errorMessage — use together for immediate validation feedback. errorMessage accepts a string or a function (validation: ValidationResult) => string.
  • validate — a custom function (value: string[]) => ValidationError | true | null | undefined. Return a string to show as the error message.
  • validationBehavior"native" (default) blocks form submission when invalid; "aria" only marks the group via ARIA attributes.
import { useState } from "react"import { Checkbox, CheckboxGroup } from "@opengovsg/oui"export const Example = () => {  const [isInvalid, setIsInvalid] = useState(true)  return (    <CheckboxGroup      isRequired      description="Select the cities you want to visit"      isInvalid={isInvalid}      label="Select cities"      onChange={(value) => {        setIsInvalid(value.length < 1)      }}    >      <Checkbox value="buenos-aires">Buenos Aires</Checkbox>      <Checkbox value="sydney">Sydney</Checkbox>      <Checkbox value="san-francisco">San Francisco</Checkbox>      <Checkbox value="london">London</Checkbox>      <Checkbox value="tokyo">Tokyo</Checkbox>    </CheckboxGroup>  )}

Accessibility

  • Each Checkbox renders as a native <input type="checkbox"> with ARIA semantics managed by React Aria.
  • CheckboxGroup uses role="group" with an aria-labelledby pointing to its label.
  • The description and error message are wired via aria-describedby automatically.
  • If no visible label is provided, pass aria-label to the Checkbox or CheckboxGroup.
  • Keyboard interaction: Space toggles the checkbox.

Slots

Slots are named regions of the component you can target with custom Tailwind classes via the classNames prop. Each slot below corresponds to a key on the classNames object.

Checkbox slots:

  • base: Checkbox wrapper — handles alignment, placement, and general appearance.
  • box: The visual pseudo-checkbox element representing the checked/unchecked/indeterminate state.
  • icon: The check or minus icon rendered inside the box when selected or indeterminate.

Custom Styles

You can customize the Checkbox component by passing custom Tailwind CSS classes to the component slots.

import { useState } from "react"import { Badge, Checkbox } from "@opengovsg/oui"import { cn } from "@opengovsg/oui-theme"export const Example = () => {  const [isSelected, setIsSelected] = useState(false)  const user = {    name: "Test Person",    role: "Software Engineer",    status: "Active",  }  return (    <Checkbox      aria-label={user.name}      classNames={{        base: cn(          "inline-flex w-full max-w-md",          "items-center justify-start bg-interaction-neutral-subtle-default",          "cursor-pointer rounded-lg gap-2 p-4 border-2 border-transparent",          "data-[selected=true]:border-interaction-main-default",        ),      }}      isSelected={isSelected}      onChange={setIsSelected}    >      <div className="flex w-full justify-between gap-2">        {user.name}        <div className="flex flex-col items-end gap-1">          <span className="prose-caption-1">{user.role}</span>          <Badge color="success" size="sm" variant="outline">            {user.status}          </Badge>        </div>      </div>    </Checkbox>  )}

Props

Checkbox

PropTypeDefaultDescription
childrenReactNode-The label rendered next to the checkbox
valuestring-The value submitted with a parent CheckboxGroup form field
isSelectedboolean-Whether the checkbox is selected (controlled)
defaultSelectedboolean-Whether the checkbox is selected by default (uncontrolled)
onChange(isSelected: boolean) => void-Called when the checkbox selection state changes
isDisabledbooleanfalseWhether the checkbox is disabled
isReadOnlybooleanfalseWhether the checkbox is read-only
isRequiredbooleanfalseWhether the checkbox is required
isInvalidbooleanfalseWhether the checkbox should display as invalid
isIndeterminatebooleanfalseWhether the checkbox is in an indeterminate state
size"sm" | "md""md"The size of the checkbox
classNamesSlotsToClassesWithRenderProps<CheckboxSlots, CheckboxRenderProps>-Custom Tailwind classes for component slots; accepts static strings or render-prop functions
classNamestring-Additional class applied to the base slot

Inherits all remaining props from React Aria's Checkbox. Notable ones: autoFocus, excludeFromTabOrder, aria-label, aria-labelledby, name.

CheckboxGroup

PropTypeDefaultDescription
childrenReactNode-The Checkbox components in the group
labelstring-The visible label for the group
descriptionstring-Helper text shown below the group
valuestring[]-The selected values (controlled)
defaultValuestring[]-The default selected values (uncontrolled)
onChange(value: string[]) => void-Called when the selection changes
isDisabledbooleanfalseWhether all checkboxes in the group are disabled
isReadOnlybooleanfalseWhether the group is read-only
isRequiredbooleanfalseWhether at least one checkbox must be selected
isInvalidbooleanfalseWhether the group should display as invalid
validate(value: string[]) => ValidationError | true | null | undefined-Custom validation function
errorMessagestring | ((validation: ValidationResult) => string)-Error message shown when the group is invalid
validationBehavior"native" | "aria""native"Whether to use native HTML form validation or ARIA-only
size"sm" | "md"-Size applied to all checkboxes in the group
classNamesSlotsToClasses<"label" | "base" | "description"> & { error?: SlotsToClasses<FieldErrorSlots> }-Custom Tailwind classes for group slots and the error message element

Inherits all remaining props from React Aria's CheckboxGroup. Notable ones: name, orientation, excludeFromTabOrder.

On this page