Ark UI Logo
Components
Checkbox

Checkbox

A control element that allows for multiple selections within a set.

Loading...

Anatomy

To set up the checkbox correctly, you'll need to understand its anatomy and how we name its parts.

Each part includes a data-part attribute to help identify them in the DOM.

Design impact on the asChild property

The Checkbox.Root element of the checkbox is a label element. This is because the checkbox is a form control and should be associated with a label to provide context and meaning to the user. Otherwise, the HTML and accessibility structure will be invalid.

If you need to use the asChild property, make sure that the label element is the direct child of the Checkbox.Root component.

Examples

Learn how to use the Checkbox component in your project. Let's take a look at the most basic example:

import { Checkbox } from '@ark-ui/react/checkbox'
import { CheckIcon } from 'lucide-react'
import styles from 'styles/checkbox.module.css'

export const Basic = () => (
  <Checkbox.Root className={styles.Root}>
    <Checkbox.Control className={styles.Control}>
      <Checkbox.Indicator className={styles.Indicator}>
        <CheckIcon />
      </Checkbox.Indicator>
    </Checkbox.Control>
    <Checkbox.Label className={styles.Label}>Checkbox</Checkbox.Label>
    <Checkbox.HiddenInput />
  </Checkbox.Root>
)

Controlled

Use the checked and onCheckedChange props to programatically control the checkbox's state.

import { Checkbox } from '@ark-ui/react/checkbox'
import { CheckIcon } from 'lucide-react'
import { useState } from 'react'
import styles from 'styles/checkbox.module.css'

export const Controlled = () => {
  const [checked, setChecked] = useState<Checkbox.CheckedState>(true)

  return (
    <Checkbox.Root className={styles.Root} checked={checked} onCheckedChange={(e) => setChecked(e.checked)}>
      <Checkbox.Control className={styles.Control}>
        <Checkbox.Indicator className={styles.Indicator}>
          <CheckIcon />
        </Checkbox.Indicator>
      </Checkbox.Control>
      <Checkbox.Label className={styles.Label}>Checkbox</Checkbox.Label>
      <Checkbox.HiddenInput />
    </Checkbox.Root>
  )
}

Default Checked

Use the defaultChecked prop to set the initial checked state in an uncontrolled manner. The checkbox will manage its own state internally.

import { Checkbox } from '@ark-ui/react/checkbox'
import { CheckIcon } from 'lucide-react'
import styles from 'styles/checkbox.module.css'

export const DefaultChecked = () => (
  <Checkbox.Root className={styles.Root} defaultChecked>
    <Checkbox.Control className={styles.Control}>
      <Checkbox.Indicator className={styles.Indicator}>
        <CheckIcon />
      </Checkbox.Indicator>
    </Checkbox.Control>
    <Checkbox.Label className={styles.Label}>Checkbox</Checkbox.Label>
    <Checkbox.HiddenInput />
  </Checkbox.Root>
)

Disabled

Use the disabled prop to make the checkbox non-interactive.

import { Checkbox } from '@ark-ui/react/checkbox'
import { CheckIcon } from 'lucide-react'
import styles from 'styles/checkbox.module.css'

export const Disabled = () => (
  <Checkbox.Root className={styles.Root} disabled>
    <Checkbox.Control className={styles.Control}>
      <Checkbox.Indicator className={styles.Indicator}>
        <CheckIcon />
      </Checkbox.Indicator>
    </Checkbox.Control>
    <Checkbox.Label className={styles.Label}>Checkbox</Checkbox.Label>
    <Checkbox.HiddenInput />
  </Checkbox.Root>
)

Indeterminate

Use the indeterminate prop to create a checkbox in an indeterminate state (partially checked).

import { Checkbox } from '@ark-ui/react/checkbox'
import { CheckIcon, MinusIcon } from 'lucide-react'
import styles from 'styles/checkbox.module.css'

export const Indeterminate = () => (
  <Checkbox.Root className={styles.Root} checked="indeterminate">
    <Checkbox.Control className={styles.Control}>
      <Checkbox.Indicator className={styles.Indicator}>
        <CheckIcon />
      </Checkbox.Indicator>
      <Checkbox.Indicator className={styles.Indicator} indeterminate>
        <MinusIcon />
      </Checkbox.Indicator>
    </Checkbox.Control>
    <Checkbox.Label className={styles.Label}>Checkbox</Checkbox.Label>
    <Checkbox.HiddenInput />
  </Checkbox.Root>
)

Programmatic Control

Use the useCheckbox hook with setChecked() to programmatically control the checkbox state.

import { Checkbox, useCheckbox } from '@ark-ui/react/checkbox'
import { CheckIcon } from 'lucide-react'
import styles from 'styles/checkbox.module.css'
import button from 'styles/button.module.css'

export const ProgrammaticControl = () => {
  const checkbox = useCheckbox()

  return (
    <>
      <div style={{ display: 'flex', gap: '10px', marginBottom: '10px' }}>
        <button type="button" onClick={() => checkbox.setChecked(true)} className={button.Root}>
          Check
        </button>
        <button type="button" onClick={() => checkbox.setChecked(false)} className={button.Root}>
          Uncheck
        </button>
      </div>

      <Checkbox.RootProvider className={styles.Root} value={checkbox}>
        <Checkbox.Control className={styles.Control}>
          <Checkbox.Indicator className={styles.Indicator}>
            <CheckIcon />
          </Checkbox.Indicator>
        </Checkbox.Control>
        <Checkbox.Label className={styles.Label}>Checkbox</Checkbox.Label>
        <Checkbox.HiddenInput />
      </Checkbox.RootProvider>
    </>
  )
}

Access Checkbox State

Use the Checkbox.Context component to access the checkbox's state and methods.

import { Checkbox } from '@ark-ui/react/checkbox'
import { CheckIcon } from 'lucide-react'
import styles from 'styles/checkbox.module.css'

export const Context = () => (
  <Checkbox.Root className={styles.Root}>
    <Checkbox.Control className={styles.Control}>
      <Checkbox.Indicator className={styles.Indicator}>
        <CheckIcon />
      </Checkbox.Indicator>
    </Checkbox.Control>
    <Checkbox.Context>
      {(checkbox) => <Checkbox.Label className={styles.Label}>Checked: {String(checkbox.checked)}</Checkbox.Label>}
    </Checkbox.Context>
    <Checkbox.HiddenInput />
  </Checkbox.Root>
)

Field

The checkbox integrates smoothly with the Field component to handle form state, helper text, and error text for proper accessibility.

import { Checkbox } from '@ark-ui/react/checkbox'
import { Field } from '@ark-ui/react/field'
import { CheckIcon, MinusIcon } from 'lucide-react'
import styles from 'styles/checkbox.module.css'
import field from 'styles/field.module.css'

export const WithField = () => (
  <Field.Root className={field.Root}>
    <Checkbox.Root className={styles.Root}>
      <Checkbox.Control className={styles.Control}>
        <Checkbox.Indicator className={styles.Indicator}>
          <CheckIcon />
        </Checkbox.Indicator>
        <Checkbox.Indicator className={styles.Indicator} indeterminate>
          <MinusIcon />
        </Checkbox.Indicator>
      </Checkbox.Control>
      <Checkbox.Label className={styles.Label}>Label</Checkbox.Label>
      <Checkbox.HiddenInput />
    </Checkbox.Root>
    <Field.HelperText className={field.HelperText}>Additional Info</Field.HelperText>
    <Field.ErrorText className={field.ErrorText}>Error Info</Field.ErrorText>
  </Field.Root>
)

Form

Pass the name and value props to the Checkbox.Root component to make the checkbox part of a form. The checkbox's value will be submitted with the form when the user submits it.

import { Checkbox } from '@ark-ui/react/checkbox'
import { CheckIcon } from 'lucide-react'
import styles from 'styles/checkbox.module.css'
import button from 'styles/button.module.css'

export const WithForm = () => (
  <form
    style={{ display: 'flex', flexDirection: 'column', gap: '1rem', alignItems: 'flex-start' }}
    onSubmit={(e) => {
      e.preventDefault()
      const formData = new FormData(e.currentTarget)
      console.log('terms:', formData.get('terms'))
    }}
  >
    <Checkbox.Root className={styles.Root} name="terms" value="accepted">
      <Checkbox.Control className={styles.Control}>
        <Checkbox.Indicator className={styles.Indicator}>
          <CheckIcon />
        </Checkbox.Indicator>
      </Checkbox.Control>
      <Checkbox.Label className={styles.Label}>I agree to the terms and conditions</Checkbox.Label>
      <Checkbox.HiddenInput />
    </Checkbox.Root>
    <button className={button.Root} data-variant="solid" type="submit">
      Submit
    </button>
  </form>
)

Root Provider

Use the useCheckbox hook to create the checkbox store and pass it to the Checkbox.RootProvider component. This allows you to have maximum control over the checkbox programmatically.

import { Checkbox, useCheckbox } from '@ark-ui/react/checkbox'
import { CheckIcon } from 'lucide-react'
import styles from 'styles/checkbox.module.css'

export const RootProvider = () => {
  const checkbox = useCheckbox()

  return (
    <Checkbox.RootProvider className={styles.Root} value={checkbox}>
      <Checkbox.Control className={styles.Control}>
        <Checkbox.Indicator className={styles.Indicator}>
          <CheckIcon />
        </Checkbox.Indicator>
      </Checkbox.Control>
      <Checkbox.Label className={styles.Label}>Checkbox</Checkbox.Label>
      <Checkbox.HiddenInput />
    </Checkbox.RootProvider>
  )
}

If you're using the Checkbox.RootProvider component, you don't need to use the Checkbox.Root component.

Group

Use the Checkbox.Group component to manage a group of checkboxes. The Checkbox.Group component manages the state of the checkboxes and provides a way to access the checked values.

import { Checkbox } from '@ark-ui/react/checkbox'
import { CheckIcon } from 'lucide-react'
import styles from 'styles/checkbox.module.css'

export const Group = () => (
  <Checkbox.Group className={styles.Group} defaultValue={['react']} name="framework">
    {items.map((item) => (
      <Checkbox.Root className={styles.Root} value={item.value} key={item.value}>
        <Checkbox.Control className={styles.Control}>
          <Checkbox.Indicator className={styles.Indicator}>
            <CheckIcon />
          </Checkbox.Indicator>
        </Checkbox.Control>
        <Checkbox.Label className={styles.Label}>{item.label}</Checkbox.Label>
        <Checkbox.HiddenInput />
      </Checkbox.Root>
    ))}
  </Checkbox.Group>
)

const items = [
  { label: 'React', value: 'react' },
  { label: 'Solid', value: 'solid' },
  { label: 'Vue', value: 'vue' },
]

Group Controlled

Use the value and onValueChange props to programmatically control the checkbox group's state. This example demonstrates how to manage selected checkboxes in an array and display the current selection.

import { Checkbox } from '@ark-ui/react/checkbox'
import { CheckIcon } from 'lucide-react'
import { useState } from 'react'
import styles from 'styles/checkbox.module.css'

export const GroupControlled = () => {
  const [value, setValue] = useState(['react'])
  return (
    <div>
      <Checkbox.Group className={styles.Group} value={value} name="framework" onValueChange={setValue}>
        {items.map((item) => (
          <Checkbox.Root className={styles.Root} value={item.value} key={item.value}>
            <Checkbox.Control className={styles.Control}>
              <Checkbox.Indicator className={styles.Indicator}>
                <CheckIcon />
              </Checkbox.Indicator>
            </Checkbox.Control>
            <Checkbox.Label className={styles.Label}>{item.label}</Checkbox.Label>
            <Checkbox.HiddenInput />
          </Checkbox.Root>
        ))}
      </Checkbox.Group>
      <pre>Selected: {JSON.stringify(value)}</pre>
    </div>
  )
}

const items = [
  { label: 'React', value: 'react' },
  { label: 'Solid', value: 'solid' },
  { label: 'Vue', value: 'vue' },
]

Group Provider

Use the useCheckboxGroup hook to create the checkbox group store and pass it to the Checkbox.GroupProvider component. This provides maximum control over the group programmatically, similar to how RootProvider works for individual checkboxes.

import { Checkbox, useCheckboxGroup } from '@ark-ui/react/checkbox'
import { CheckIcon } from 'lucide-react'
import styles from 'styles/checkbox.module.css'

export const GroupProvider = () => {
  const group = useCheckboxGroup({
    defaultValue: ['react'],
    name: 'framework',
  })

  return (
    <Checkbox.GroupProvider className={styles.Group} value={group}>
      {items.map((item) => (
        <Checkbox.Root className={styles.Root} value={item.value} key={item.value}>
          <Checkbox.Control className={styles.Control}>
            <Checkbox.Indicator className={styles.Indicator}>
              <CheckIcon />
            </Checkbox.Indicator>
          </Checkbox.Control>
          <Checkbox.Label className={styles.Label}>{item.label}</Checkbox.Label>
          <Checkbox.HiddenInput />
        </Checkbox.Root>
      ))}
    </Checkbox.GroupProvider>
  )
}

const items = [
  { label: 'React', value: 'react' },
  { label: 'Solid', value: 'solid' },
  { label: 'Vue', value: 'vue' },
]

Group + Form

Use the Checkbox.Group component within a form to handle multiple checkbox values with form submission. The name prop ensures all selected values are collected as an array when the form is submitted using FormData.getAll().

import { Checkbox } from '@ark-ui/react/checkbox'
import { CheckIcon } from 'lucide-react'
import styles from 'styles/checkbox.module.css'
import button from 'styles/button.module.css'

export const GroupWithForm = () => (
  <form
    onSubmit={(e) => {
      e.preventDefault()
      console.log(new FormData(e.currentTarget).getAll('framework'))
    }}
  >
    <Checkbox.Group className={styles.Group} defaultValue={['react']} name="framework">
      {items.map((item) => (
        <Checkbox.Root className={styles.Root} value={item.value} key={item.value}>
          <Checkbox.Control className={styles.Control}>
            <Checkbox.Indicator className={styles.Indicator}>
              <CheckIcon />
            </Checkbox.Indicator>
          </Checkbox.Control>
          <Checkbox.Label className={styles.Label}>{item.label}</Checkbox.Label>
          <Checkbox.HiddenInput />
        </Checkbox.Root>
      ))}
    </Checkbox.Group>
    <button className={button.Root} type="submit">
      Submit
    </button>
  </form>
)

const items = [
  { label: 'React', value: 'react' },
  { label: 'Solid', value: 'solid' },
  { label: 'Vue', value: 'vue' },
]

Group + Invalid

Use the invalid prop on Checkbox.Group to mark the entire group as invalid for validation purposes. This applies the invalid state to all checkboxes within the group.

import { Checkbox } from '@ark-ui/react/checkbox'
import { CheckIcon } from 'lucide-react'
import styles from 'styles/checkbox.module.css'

export const GroupWithInvalid = () => (
  <Checkbox.Group className={styles.Group} invalid>
    {items.map((item) => (
      <Checkbox.Root className={styles.Root} value={item.value} key={item.value}>
        <Checkbox.Control className={styles.Control}>
          <Checkbox.Indicator className={styles.Indicator}>
            <CheckIcon />
          </Checkbox.Indicator>
        </Checkbox.Control>
        <Checkbox.Label className={styles.Label}>{item.label}</Checkbox.Label>
        <Checkbox.HiddenInput />
      </Checkbox.Root>
    ))}
  </Checkbox.Group>
)

const items = [
  { label: 'React', value: 'react' },
  { label: 'Solid', value: 'solid' },
  { label: 'Vue', value: 'vue' },
]

Group + Select All

Implement a "select all" checkbox that controls all checkboxes within a group. The parent checkbox automatically shows an indeterminate state when some (but not all) items are selected, and becomes fully checked when all items are selected.

import { Checkbox } from '@ark-ui/react/checkbox'
import { CheckIcon, MinusIcon } from 'lucide-react'
import { useState } from 'react'
import styles from 'styles/checkbox.module.css'

const CheckboxItem = (props: Checkbox.RootProps) => {
  return (
    <Checkbox.Root className={styles.Root} {...props}>
      <Checkbox.Control className={styles.Control}>
        <Checkbox.Indicator className={styles.Indicator}>
          <CheckIcon />
        </Checkbox.Indicator>
        <Checkbox.Indicator className={styles.Indicator} indeterminate>
          <MinusIcon />
        </Checkbox.Indicator>
      </Checkbox.Control>
      <Checkbox.Label className={styles.Label}>{props.children}</Checkbox.Label>
      <Checkbox.HiddenInput />
    </Checkbox.Root>
  )
}

export const GroupWithSelectAll = () => {
  const [value, setValue] = useState<string[]>([])

  const handleSelectAll = (checked: boolean) => {
    setValue(checked ? items.map((item) => item.value) : [])
  }

  const allSelected = value.length === items.length
  const indeterminate = value.length > 0 && value.length < items.length

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
      <output>Selected: {JSON.stringify(value)}</output>

      <CheckboxItem
        value="all"
        checked={indeterminate ? 'indeterminate' : allSelected}
        onCheckedChange={(e) => handleSelectAll(!!e.checked)}
      >
        JSX Frameworks
      </CheckboxItem>

      <Checkbox.Group
        className={styles.Group}
        style={{ marginInlineStart: '1rem' }}
        value={value}
        name="framework"
        onValueChange={setValue}
      >
        {items.map((item) => (
          <CheckboxItem value={item.value} key={item.value}>
            {item.label}
          </CheckboxItem>
        ))}
      </Checkbox.Group>
    </div>
  )
}

const items = [
  { label: 'React', value: 'react' },
  { label: 'Solid', value: 'solid' },
  { label: 'Vue', value: 'vue' },
]

Group + Fieldset

Use the Fieldset component with Checkbox.Group to provide semantic grouping with legend, helper text, and error text support.

import { Checkbox } from '@ark-ui/react/checkbox'
import { Fieldset } from '@ark-ui/react/fieldset'
import { CheckIcon } from 'lucide-react'
import styles from 'styles/checkbox.module.css'
import fieldset from 'styles/fieldset.module.css'

export const GroupWithFieldset = () => (
  <Fieldset.Root className={fieldset.Root}>
    <Fieldset.Legend className={fieldset.Legend}>Select frameworks</Fieldset.Legend>
    <Checkbox.Group className={styles.Group} defaultValue={['react']} name="framework">
      {items.map((item) => (
        <Checkbox.Root className={styles.Root} value={item.value} key={item.value}>
          <Checkbox.Control className={styles.Control}>
            <Checkbox.Indicator className={styles.Indicator}>
              <CheckIcon />
            </Checkbox.Indicator>
          </Checkbox.Control>
          <Checkbox.Label className={styles.Label}>{item.label}</Checkbox.Label>
          <Checkbox.HiddenInput />
        </Checkbox.Root>
      ))}
    </Checkbox.Group>
    <Fieldset.HelperText className={fieldset.HelperText}>Choose your preferred frameworks</Fieldset.HelperText>
  </Fieldset.Root>
)

const items = [
  { label: 'React', value: 'react' },
  { label: 'Solid', value: 'solid' },
  { label: 'Vue', value: 'vue' },
]

API Reference

Props

Root

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
checked
CheckedState

The controlled checked state of the checkbox

defaultChecked
CheckedState

The initial checked state of the checkbox when rendered. Use when you don't need to control the checked state of the checkbox.

disabled
boolean

Whether the checkbox is disabled

form
string

The id of the form that the checkbox belongs to.

id
string

The unique identifier of the machine.

ids
Partial<{ root: string; hiddenInput: string; control: string; label: string }>

The ids of the elements in the checkbox. Useful for composition.

invalid
boolean

Whether the checkbox is invalid

name
string

The name of the input field in a checkbox. Useful for form submission.

onCheckedChange
(details: CheckedChangeDetails) => void

The callback invoked when the checked state changes.

readOnly
boolean

Whether the checkbox is read-only

required
boolean

Whether the checkbox is required

value'on'
string

The value of checkbox input. Useful for form submission.

Data AttributeValue
[data-active]Present when active or pressed
[data-focus]Present when focused
[data-focus-visible]Present when focused with keyboard
[data-readonly]Present when read-only
[data-hover]Present when hovered
[data-disabled]Present when disabled
[data-state]"indeterminate" | "checked" | "unchecked"
[data-invalid]Present when invalid
[data-required]Present when required

Control

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
Data AttributeValue
[data-active]Present when active or pressed
[data-focus]Present when focused
[data-focus-visible]Present when focused with keyboard
[data-readonly]Present when read-only
[data-hover]Present when hovered
[data-disabled]Present when disabled
[data-state]"indeterminate" | "checked" | "unchecked"
[data-invalid]Present when invalid
[data-required]Present when required

Group

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
defaultValue
string[]

The initial value of `value` when uncontrolled

disabled
boolean

If `true`, the checkbox group is disabled

invalid
boolean

If `true`, the checkbox group is invalid

name
string

The name of the input fields in the checkbox group (Useful for form submission).

onValueChange
(value: string[]) => void

The callback to call when the value changes

readOnly
boolean

If `true`, the checkbox group is read-only

value
string[]

The controlled value of the checkbox group

GroupProvider

PropDefaultType
value
UseCheckboxGroupContext

asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.

HiddenInput

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.

Indicator

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
indeterminate
boolean

Data AttributeValue
[data-active]Present when active or pressed
[data-focus]Present when focused
[data-focus-visible]Present when focused with keyboard
[data-readonly]Present when read-only
[data-hover]Present when hovered
[data-disabled]Present when disabled
[data-state]"indeterminate" | "checked" | "unchecked"
[data-invalid]Present when invalid
[data-required]Present when required

Label

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
Data AttributeValue
[data-active]Present when active or pressed
[data-focus]Present when focused
[data-focus-visible]Present when focused with keyboard
[data-readonly]Present when read-only
[data-hover]Present when hovered
[data-disabled]Present when disabled
[data-state]"indeterminate" | "checked" | "unchecked"
[data-invalid]Present when invalid
[data-required]Present when required

RootProvider

PropDefaultType
value
UseCheckboxReturn

asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.

Context

These are the properties available when using Checkbox.Context, useCheckboxContext hook or useCheckbox hook.

API

PropertyType
checked
boolean

Whether the checkbox is checked

disabled
boolean

Whether the checkbox is disabled

indeterminate
boolean

Whether the checkbox is indeterminate

focused
boolean

Whether the checkbox is focused

checkedState
CheckedState

The checked state of the checkbox

setChecked
(checked: CheckedState) => void

Function to set the checked state of the checkbox

toggleChecked
VoidFunction

Function to toggle the checked state of the checkbox

Accessibility

Complies with the Checkbox WAI-ARIA design pattern.

Keyboard Support

KeyDescription
Space
Toggle the checkbox