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.jsonpnpm dlx shadcn@latest add https://oui.open.gov.sg/r/checkbox.jsonnpx shadcn@latest add https://oui.open.gov.sg/r/checkbox.jsonbunx --bun shadcn@latest add https://oui.open.gov.sg/r/checkbox.jsonCheckbox 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.errorMessageaccepts astringor 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
Checkboxrenders as a native<input type="checkbox">with ARIA semantics managed by React Aria. CheckboxGroupusesrole="group"with anaria-labelledbypointing to its label.- The description and error message are wired via
aria-describedbyautomatically. - If no visible label is provided, pass
aria-labelto theCheckboxorCheckboxGroup. - 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
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | - | The label rendered next to the checkbox |
value | string | - | The value submitted with a parent CheckboxGroup form field |
isSelected | boolean | - | Whether the checkbox is selected (controlled) |
defaultSelected | boolean | - | Whether the checkbox is selected by default (uncontrolled) |
onChange | (isSelected: boolean) => void | - | Called when the checkbox selection state changes |
isDisabled | boolean | false | Whether the checkbox is disabled |
isReadOnly | boolean | false | Whether the checkbox is read-only |
isRequired | boolean | false | Whether the checkbox is required |
isInvalid | boolean | false | Whether the checkbox should display as invalid |
isIndeterminate | boolean | false | Whether the checkbox is in an indeterminate state |
size | "sm" | "md" | "md" | The size of the checkbox |
classNames | SlotsToClassesWithRenderProps<CheckboxSlots, CheckboxRenderProps> | - | Custom Tailwind classes for component slots; accepts static strings or render-prop functions |
className | string | - | 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
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | - | The Checkbox components in the group |
label | string | - | The visible label for the group |
description | string | - | Helper text shown below the group |
value | string[] | - | The selected values (controlled) |
defaultValue | string[] | - | The default selected values (uncontrolled) |
onChange | (value: string[]) => void | - | Called when the selection changes |
isDisabled | boolean | false | Whether all checkboxes in the group are disabled |
isReadOnly | boolean | false | Whether the group is read-only |
isRequired | boolean | false | Whether at least one checkbox must be selected |
isInvalid | boolean | false | Whether the group should display as invalid |
validate | (value: string[]) => ValidationError | true | null | undefined | - | Custom validation function |
errorMessage | string | ((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 |
classNames | SlotsToClasses<"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.