Checkbox
A control element that allows for multiple selections within a set.
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-partattribute 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
asChildproperty, make sure that thelabelelement is the direct child of theCheckbox.Rootcomponent.
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>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
import styles from 'styles/checkbox.module.css'
export const Basic = () => (
<Checkbox.Root class={styles.Root}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>Checkbox</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
import styles from 'styles/checkbox.module.css'
</script>
<template>
<Checkbox.Root :class="styles.Root">
<Checkbox.Control :class="styles.Control">
<Checkbox.Indicator :class="styles.Indicator">
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label :class="styles.Label">Checkbox</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
import styles from 'styles/checkbox.module.css'
</script>
<Checkbox.Root class={styles.Root}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={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>
)
}
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import styles from 'styles/checkbox.module.css'
export const Controlled = () => {
const [checked, setChecked] = createSignal<Checkbox.CheckedState>(true)
return (
<Checkbox.Root class={styles.Root} checked={checked()} onCheckedChange={(e) => setChecked(e.checked)}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>Checkbox</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
}
<script setup lang="ts">
import { Checkbox, type CheckboxCheckedState } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
import { ref } from 'vue'
import styles from 'styles/checkbox.module.css'
const checked = ref<CheckboxCheckedState>(true)
</script>
<template>
<Checkbox.Root :class="styles.Root" v-model:checked="checked">
<Checkbox.Control :class="styles.Control">
<Checkbox.Indicator :class="styles.Indicator">
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label :class="styles.Label">Checkbox</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
import styles from 'styles/checkbox.module.css'
let checked = $state(false)
</script>
<Checkbox.Root class={styles.Root} bind:checked>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={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>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
import styles from 'styles/checkbox.module.css'
export const DefaultChecked = () => (
<Checkbox.Root class={styles.Root} defaultChecked>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>Checkbox</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
import styles from 'styles/checkbox.module.css'
</script>
<template>
<Checkbox.Root :class="styles.Root" defaultChecked>
<Checkbox.Control :class="styles.Control">
<Checkbox.Indicator :class="styles.Indicator">
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label :class="styles.Label">Checkbox</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
import styles from 'styles/checkbox.module.css'
</script>
<Checkbox.Root class={styles.Root} defaultChecked>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={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>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
import styles from 'styles/checkbox.module.css'
export const Disabled = () => (
<Checkbox.Root class={styles.Root} disabled>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>Checkbox</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
import styles from 'styles/checkbox.module.css'
</script>
<template>
<Checkbox.Root :class="styles.Root" disabled>
<Checkbox.Control :class="styles.Control">
<Checkbox.Indicator :class="styles.Indicator">
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label :class="styles.Label">Checkbox</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
import styles from 'styles/checkbox.module.css'
</script>
<Checkbox.Root class={styles.Root} disabled>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={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>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon, MinusIcon } from 'lucide-solid'
import styles from 'styles/checkbox.module.css'
export const Indeterminate = () => (
<Checkbox.Root class={styles.Root} checked="indeterminate">
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator class={styles.Indicator} indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>Checkbox</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon, MinusIcon } from 'lucide-vue-next'
import styles from 'styles/checkbox.module.css'
</script>
<template>
<Checkbox.Root :class="styles.Root" checked="indeterminate">
<Checkbox.Control :class="styles.Control">
<Checkbox.Indicator :class="styles.Indicator">
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator :class="styles.Indicator" indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label :class="styles.Label">Checkbox</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon, MinusIcon } from 'lucide-svelte'
import styles from 'styles/checkbox.module.css'
</script>
<Checkbox.Root class={styles.Root} checked="indeterminate">
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator class={styles.Indicator} indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={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>
</>
)
}
import { Checkbox, useCheckbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
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', 'margin-bottom': '10px' }}>
<button type="button" onClick={() => checkbox().setChecked(true)} class={button.Root}>
Check
</button>
<button type="button" onClick={() => checkbox().setChecked(false)} class={button.Root}>
Uncheck
</button>
</div>
<Checkbox.RootProvider class={styles.Root} value={checkbox}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>Checkbox</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.RootProvider>
</>
)
}
<script setup lang="ts">
import { Checkbox, useCheckbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
import styles from 'styles/checkbox.module.css'
import button from 'styles/button.module.css'
const checkbox = useCheckbox()
</script>
<template>
<div style="display: flex; gap: 0.5rem; margin-bottom: 1rem">
<button :class="button.Root" type="button" @click="() => checkbox.setChecked(true)">Check</button>
<button :class="button.Root" type="button" @click="() => checkbox.setChecked(false)">Uncheck</button>
</div>
<Checkbox.RootProvider :class="styles.Root" :value="checkbox">
<Checkbox.Control :class="styles.Control">
<Checkbox.Indicator :class="styles.Indicator">
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label :class="styles.Label">Checkbox</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.RootProvider>
</template>
<script lang="ts">
import { Checkbox, useCheckbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
import styles from 'styles/checkbox.module.css'
import button from 'styles/button.module.css'
const checkbox = useCheckbox()
</script>
<div style="display: flex; gap: 10px; margin-bottom: 10px;">
<button type="button" class={button.Root} onclick={() => checkbox().setChecked(true)}>Check</button>
<button type="button" class={button.Root} onclick={() => checkbox().setChecked(false)}>Uncheck</button>
</div>
<Checkbox.RootProvider class={styles.Root} value={checkbox}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={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>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
import styles from 'styles/checkbox.module.css'
export const Context = () => (
<Checkbox.Root class={styles.Root}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Context>
{(checkbox) => <Checkbox.Label class={styles.Label}>Checked: {String(checkbox().checked)}</Checkbox.Label>}
</Checkbox.Context>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
import styles from 'styles/checkbox.module.css'
</script>
<template>
<Checkbox.Root :class="styles.Root">
<Checkbox.Control :class="styles.Control">
<Checkbox.Indicator :class="styles.Indicator">
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Context v-slot="checkbox">
<Checkbox.Label :class="styles.Label">Checked: {{ String(checkbox.checked) }}</Checkbox.Label>
</Checkbox.Context>
<Checkbox.HiddenInput />
</Checkbox.Root>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
import styles from 'styles/checkbox.module.css'
</script>
<Checkbox.Root class={styles.Root}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Context>
{#snippet render(api)}
<Checkbox.Label class={styles.Label}>Checked: {String(api().checked)}</Checkbox.Label>
{/snippet}
</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>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { Field } from '@ark-ui/solid/field'
import { CheckIcon, MinusIcon } from 'lucide-solid'
import styles from 'styles/checkbox.module.css'
import field from 'styles/field.module.css'
export const WithField = () => (
<Field.Root class={field.Root}>
<Checkbox.Root class={styles.Root}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator class={styles.Indicator} indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>Label</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
<Field.HelperText class={field.HelperText}>Additional Info</Field.HelperText>
<Field.ErrorText class={field.ErrorText}>Error Info</Field.ErrorText>
</Field.Root>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { Field } from '@ark-ui/vue/field'
import { CheckIcon, MinusIcon } from 'lucide-vue-next'
import styles from 'styles/checkbox.module.css'
import field from 'styles/field.module.css'
</script>
<template>
<Field.Root :class="field.Root">
<Checkbox.Root :class="styles.Root">
<Checkbox.Control :class="styles.Control">
<Checkbox.Indicator :class="styles.Indicator">
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator :class="styles.Indicator" indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label :class="styles.Label">Label</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
<Field.HelperText :class="field.HelperText">Additional Info</Field.HelperText>
<Field.ErrorText :class="field.ErrorText">Error Info</Field.ErrorText>
</Field.Root>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { Field } from '@ark-ui/svelte/field'
import { CheckIcon, MinusIcon } from 'lucide-svelte'
import styles from 'styles/checkbox.module.css'
import field from 'styles/field.module.css'
</script>
<Field.Root class={field.Root}>
<Checkbox.Root class={styles.Root}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator class={styles.Indicator} indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>Label</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
<Field.HelperText class={field.HelperText}>Additional Info</Field.HelperText>
<Field.ErrorText class={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>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
import styles from 'styles/checkbox.module.css'
import button from 'styles/button.module.css'
export const WithForm = () => (
<form
style={{ display: 'flex', 'flex-direction': 'column', gap: '1rem', 'align-items': 'flex-start' }}
onSubmit={(e) => {
e.preventDefault()
const formData = new FormData(e.currentTarget)
console.log('terms:', formData.get('terms'))
}}
>
<Checkbox.Root class={styles.Root} name="terms" value="accepted">
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>I agree to the terms and conditions</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
<button class={button.Root} data-variant="solid" type="submit">
Submit
</button>
</form>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
import styles from 'styles/checkbox.module.css'
import button from 'styles/button.module.css'
const handleSubmit = (event: Event) => {
event.preventDefault()
const formData = new FormData(event.target as HTMLFormElement)
console.log('terms:', formData.get('terms'))
}
</script>
<template>
<form style="display: flex; flex-direction: column; gap: 1rem; align-items: flex-start" @submit="handleSubmit">
<Checkbox.Root :class="styles.Root" name="terms" value="accepted">
<Checkbox.Control :class="styles.Control">
<Checkbox.Indicator :class="styles.Indicator">
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label :class="styles.Label">I agree to the terms and conditions</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
<button :class="button.Root" data-variant="solid" type="submit">Submit</button>
</form>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
import styles from 'styles/checkbox.module.css'
import button from 'styles/button.module.css'
</script>
<form
style="display: flex; flex-direction: column; gap: 1rem; align-items: flex-start;"
onsubmit={(e) => {
e.preventDefault()
const formData = new FormData(e.currentTarget)
console.log('terms:', formData.get('terms'))
}}
>
<Checkbox.Root class={styles.Root} name="terms" value="accepted">
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>I agree to the terms and conditions</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
<button class={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>
)
}
import { Checkbox, useCheckbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
import styles from 'styles/checkbox.module.css'
export const RootProvider = () => {
const checkbox = useCheckbox()
return (
<Checkbox.RootProvider class={styles.Root} value={checkbox}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>Checkbox</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.RootProvider>
)
}
<script setup lang="ts">
import { Checkbox, useCheckbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
import styles from 'styles/checkbox.module.css'
const checkbox = useCheckbox()
</script>
<template>
<Checkbox.RootProvider :class="styles.Root" :value="checkbox">
<Checkbox.Control :class="styles.Control">
<Checkbox.Indicator :class="styles.Indicator">
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label :class="styles.Label">Checkbox</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.RootProvider>
</template>
<script lang="ts">
import { Checkbox, useCheckbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
import styles from 'styles/checkbox.module.css'
const checkbox = useCheckbox()
</script>
<Checkbox.RootProvider class={styles.Root} value={checkbox}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>Checkbox</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.RootProvider>
If you're using the
Checkbox.RootProvidercomponent, you don't need to use theCheckbox.Rootcomponent.
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' },
]
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
import { For } from 'solid-js'
import styles from 'styles/checkbox.module.css'
export const Group = () => (
<Checkbox.Group class={styles.Group} defaultValue={['react']} name="framework">
<For each={items}>
{(item) => (
<Checkbox.Root class={styles.Root} value={item.value}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>{item.label}</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
)}
</For>
</Checkbox.Group>
)
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
import styles from 'styles/checkbox.module.css'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
</script>
<template>
<Checkbox.Group :class="styles.Group" :defaultValue="['react']" name="framework">
<Checkbox.Root :class="styles.Root" v-for="item in items" :value="item.value" :key="item.value">
<Checkbox.Control :class="styles.Control">
<Checkbox.Indicator :class="styles.Indicator">
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label :class="styles.Label">{{ item.label }}</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
</Checkbox.Group>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
import styles from 'styles/checkbox.module.css'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
</script>
<Checkbox.Group class={styles.Group} defaultValue={['react']} name="framework">
{#each items as item (item.value)}
<Checkbox.Root class={styles.Root} value={item.value}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>{item.label}</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
{/each}
</Checkbox.Group>
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' },
]
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
import { createSignal, For } from 'solid-js'
import styles from 'styles/checkbox.module.css'
export const GroupControlled = () => {
const [value, setValue] = createSignal(['react'])
return (
<div>
<Checkbox.Group class={styles.Group} value={value} onValueChange={setValue} name="framework">
<For each={items}>
{(item) => (
<Checkbox.Root class={styles.Root} value={item.value}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>{item.label}</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
)}
</For>
</Checkbox.Group>
<pre>Selected: {JSON.stringify(value())}</pre>
</div>
)
}
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
import { ref } from 'vue'
import styles from 'styles/checkbox.module.css'
const value = ref(['react'])
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
</script>
<template>
<div>
<Checkbox.Group :class="styles.Group" v-model="value" name="framework">
<Checkbox.Root :class="styles.Root" v-for="item in items" :value="item.value" :key="item.value">
<Checkbox.Control :class="styles.Control">
<Checkbox.Indicator :class="styles.Indicator">
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label :class="styles.Label">{{ item.label }}</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
</Checkbox.Group>
<pre>Selected: {{ JSON.stringify(value) }}</pre>
</div>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
import styles from 'styles/checkbox.module.css'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
let value = $state(['react'])
</script>
<div>
<Checkbox.Group class={styles.Group} bind:value name="framework">
{#each items as item (item.value)}
<Checkbox.Root class={styles.Root} value={item.value}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>{item.label}</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
{/each}
</Checkbox.Group>
<pre>Selected: {JSON.stringify(value)}</pre>
</div>
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' },
]
import { Checkbox, useCheckboxGroup } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
import { For } from 'solid-js'
import styles from 'styles/checkbox.module.css'
export const GroupProvider = () => {
const group = useCheckboxGroup({
defaultValue: ['react'],
name: 'framework',
})
return (
<Checkbox.GroupProvider class={styles.Group} value={group}>
<For each={items}>
{(item) => (
<Checkbox.Root class={styles.Root} value={item.value}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>{item.label}</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
)}
</For>
</Checkbox.GroupProvider>
)
}
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
<script setup lang="ts">
import { Checkbox, useCheckboxGroup } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
import styles from 'styles/checkbox.module.css'
const group = useCheckboxGroup({
defaultValue: ['react'],
name: 'framework',
})
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
</script>
<template>
<Checkbox.GroupProvider :class="styles.Group" :value="group">
<Checkbox.Root :class="styles.Root" v-for="item in items" :value="item.value" :key="item.value">
<Checkbox.Control :class="styles.Control">
<Checkbox.Indicator :class="styles.Indicator">
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label :class="styles.Label">{{ item.label }}</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
</Checkbox.GroupProvider>
</template>
<script lang="ts">
import { Checkbox, useCheckboxGroup } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
import styles from 'styles/checkbox.module.css'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
const group = useCheckboxGroup({
defaultValue: ['react'],
name: 'framework',
})
</script>
<Checkbox.GroupProvider class={styles.Group} value={group}>
{#each items as item (item.value)}
<Checkbox.Root class={styles.Root} value={item.value}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>{item.label}</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
{/each}
</Checkbox.GroupProvider>
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' },
]
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
import { For } from 'solid-js'
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 class={styles.Group} defaultValue={['react']} name="framework">
<For each={items}>
{(item) => (
<Checkbox.Root class={styles.Root} value={item.value}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>{item.label}</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
)}
</For>
</Checkbox.Group>
<button class={button.Root} type="submit">
Submit
</button>
</form>
)
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
import styles from 'styles/checkbox.module.css'
import button from 'styles/button.module.css'
const handleSubmit = (event: Event) => {
event.preventDefault()
const formData = new FormData(event.target as HTMLFormElement)
console.log(formData.getAll('framework'))
}
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
</script>
<template>
<form @submit="handleSubmit">
<Checkbox.Group :class="styles.Group" :defaultValue="['react']" name="framework">
<Checkbox.Root :class="styles.Root" v-for="item in items" :key="item.value" :value="item.value">
<Checkbox.Control :class="styles.Control">
<Checkbox.Indicator :class="styles.Indicator">
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label :class="styles.Label">{{ item.label }}</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
</Checkbox.Group>
<button :class="button.Root" type="submit">Submit</button>
</form>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
import styles from 'styles/checkbox.module.css'
import button from 'styles/button.module.css'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
</script>
<form
onsubmit={(e) => {
e.preventDefault()
console.log(new FormData(e.currentTarget).getAll('framework'))
}}
>
<Checkbox.Group class={styles.Group} defaultValue={['react']} name="framework">
{#each items as item (item.value)}
<Checkbox.Root class={styles.Root} value={item.value}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>{item.label}</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
{/each}
</Checkbox.Group>
<button class={button.Root} type="submit">Submit</button>
</form>
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' },
]
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
import { For } from 'solid-js'
import styles from 'styles/checkbox.module.css'
export const GroupWithInvalid = () => (
<Checkbox.Group class={styles.Group} invalid>
<For each={items}>
{(item) => (
<Checkbox.Root class={styles.Root} value={item.value}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>{item.label}</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
)}
</For>
</Checkbox.Group>
)
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
import styles from 'styles/checkbox.module.css'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
</script>
<template>
<Checkbox.Group :class="styles.Group" invalid>
<Checkbox.Root :class="styles.Root" v-for="item in items" :key="item.value" :value="item.value">
<Checkbox.Control :class="styles.Control">
<Checkbox.Indicator :class="styles.Indicator">
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label :class="styles.Label">{{ item.label }}</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
</Checkbox.Group>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
import styles from 'styles/checkbox.module.css'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
</script>
<Checkbox.Group class={styles.Group} invalid>
{#each items as item (item.value)}
<Checkbox.Root class={styles.Root} value={item.value}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>{item.label}</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
{/each}
</Checkbox.Group>
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' },
]
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon, MinusIcon } from 'lucide-solid'
import { For, createSignal, createMemo } from 'solid-js'
import styles from 'styles/checkbox.module.css'
const CheckboxItem = (props: Checkbox.RootProps) => (
<Checkbox.Root class={styles.Root} {...props}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator class={styles.Indicator} indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>{props.children}</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
export const GroupWithSelectAll = () => {
const [value, setValue] = createSignal<string[]>([])
const handleSelectAll = (checked: boolean) => {
setValue(checked ? items.map((item) => item.value) : [])
}
const allSelected = createMemo(() => value().length === items.length)
const indeterminate = createMemo(() => value().length > 0 && value().length < items.length)
return (
<div style={{ display: 'flex', 'flex-direction': 'column', gap: '10px' }}>
<output>Selected: {JSON.stringify(value())}</output>
<CheckboxItem
checked={indeterminate() ? 'indeterminate' : allSelected()}
onCheckedChange={(details) => handleSelectAll(!!details.checked)}
>
JSX Frameworks
</CheckboxItem>
<Checkbox.Group
class={styles.Group}
style={{ 'margin-inline-start': '1rem' }}
value={value}
onValueChange={setValue}
name="framework"
>
<For each={items}>{(item) => <CheckboxItem value={item.value}>{item.label}</CheckboxItem>}</For>
</Checkbox.Group>
</div>
)
}
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon, MinusIcon } from 'lucide-vue-next'
import { computed, ref } from 'vue'
import styles from 'styles/checkbox.module.css'
const value = ref<string[]>([])
const handleSelectAll = (checked: boolean) => {
value.value = checked ? items.map((item) => item.value) : []
}
const allSelected = computed(() => value.value.length === items.length)
const indeterminate = computed(() => value.value.length > 0 && value.value.length < items.length)
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 10px">
<output>Selected: {{ JSON.stringify(value) }}</output>
<Checkbox.Root
:class="styles.Root"
:checked="indeterminate ? 'indeterminate' : allSelected"
@checked-change="(e) => handleSelectAll(!!e.checked)"
>
<Checkbox.Control :class="styles.Control">
<Checkbox.Indicator :class="styles.Indicator">
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator :class="styles.Indicator" indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label :class="styles.Label">JSX Frameworks</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
<Checkbox.Group :class="styles.Group" style="margin-inline-start: 1rem" v-model="value" name="framework">
<Checkbox.Root :class="styles.Root" v-for="item in items" :key="item.value" :value="item.value">
<Checkbox.Control :class="styles.Control">
<Checkbox.Indicator :class="styles.Indicator">
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator :class="styles.Indicator" indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label :class="styles.Label">{{ item.label }}</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
</Checkbox.Group>
</div>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon, MinusIcon } from 'lucide-svelte'
import styles from 'styles/checkbox.module.css'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
let value = $state<string[]>([])
function handleSelectAll(checked: boolean) {
value = checked ? items.map((item) => item.value) : []
}
const allSelected = $derived(value.length === items.length)
const indeterminate = $derived(value.length > 0 && value.length < items.length)
</script>
{#snippet CheckboxItem(item: (typeof items)[number])}
<Checkbox.Root class={styles.Root} value={item.value}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator class={styles.Indicator} indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>{item.label}</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
{/snippet}
<div style="display: flex; flex-direction: column; gap: 10px;">
<output>Selected: {JSON.stringify(value)}</output>
<Checkbox.Root
class={styles.Root}
checked={indeterminate ? 'indeterminate' : allSelected}
onCheckedChange={(e) => handleSelectAll(!!e.checked)}
>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator class={styles.Indicator} indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>JSX Frameworks</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
<Checkbox.Group class={styles.Group} style="margin-inline-start: 1rem;" bind:value name="framework">
{#each items as item (item.value)}
{@render CheckboxItem(item)}
{/each}
</Checkbox.Group>
</div>
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' },
]
import { Checkbox } from '@ark-ui/solid/checkbox'
import { Fieldset } from '@ark-ui/solid/fieldset'
import { CheckIcon } from 'lucide-solid'
import { For } from 'solid-js'
import styles from 'styles/checkbox.module.css'
import fieldset from 'styles/fieldset.module.css'
export const GroupWithFieldset = () => (
<Fieldset.Root class={fieldset.Root}>
<Fieldset.Legend class={fieldset.Legend}>Select frameworks</Fieldset.Legend>
<Checkbox.Group class={styles.Group} defaultValue={['react']} name="framework">
<For each={items}>
{(item) => (
<Checkbox.Root class={styles.Root} value={item.value}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>{item.label}</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
)}
</For>
</Checkbox.Group>
<Fieldset.HelperText class={fieldset.HelperText}>Choose your preferred frameworks</Fieldset.HelperText>
</Fieldset.Root>
)
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { Fieldset } from '@ark-ui/vue/fieldset'
import { CheckIcon } from 'lucide-vue-next'
import styles from 'styles/checkbox.module.css'
import fieldset from 'styles/fieldset.module.css'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
</script>
<template>
<Fieldset.Root :class="fieldset.Root">
<Fieldset.Legend :class="fieldset.Legend">Select frameworks</Fieldset.Legend>
<Checkbox.Group :class="styles.Group" :defaultValue="['react']" name="framework">
<Checkbox.Root :class="styles.Root" v-for="item in items" :key="item.value" :value="item.value">
<Checkbox.Control :class="styles.Control">
<Checkbox.Indicator :class="styles.Indicator">
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label :class="styles.Label">{{ item.label }}</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
</Checkbox.Group>
<Fieldset.HelperText :class="fieldset.HelperText">Choose your preferred frameworks</Fieldset.HelperText>
</Fieldset.Root>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { Fieldset } from '@ark-ui/svelte/fieldset'
import { CheckIcon } from 'lucide-svelte'
import styles from 'styles/checkbox.module.css'
import fieldset from 'styles/fieldset.module.css'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
</script>
<Fieldset.Root class={fieldset.Root}>
<Fieldset.Legend class={fieldset.Legend}>Select frameworks</Fieldset.Legend>
<Checkbox.Group class={styles.Group} defaultValue={['react']} name="framework">
{#each items as item (item.value)}
<Checkbox.Root class={styles.Root} value={item.value}>
<Checkbox.Control class={styles.Control}>
<Checkbox.Indicator class={styles.Indicator}>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label class={styles.Label}>{item.label}</Checkbox.Label>
<Checkbox.HiddenInput />
</Checkbox.Root>
{/each}
</Checkbox.Group>
<Fieldset.HelperText class={fieldset.HelperText}>Choose your preferred frameworks</Fieldset.HelperText>
</Fieldset.Root>
API Reference
Props
Root
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
checked | CheckedStateThe controlled checked state of the checkbox | |
defaultChecked | CheckedStateThe initial checked state of the checkbox when rendered. Use when you don't need to control the checked state of the checkbox. | |
disabled | booleanWhether the checkbox is disabled | |
form | stringThe id of the form that the checkbox belongs to. | |
id | stringThe 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 | booleanWhether the checkbox is invalid | |
name | stringThe name of the input field in a checkbox. Useful for form submission. | |
onCheckedChange | (details: CheckedChangeDetails) => voidThe callback invoked when the checked state changes. | |
readOnly | booleanWhether the checkbox is read-only | |
required | booleanWhether the checkbox is required | |
value | 'on' | stringThe value of checkbox input. Useful for form submission. |
| Data Attribute | Value |
|---|---|
[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
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[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
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse 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 | booleanIf `true`, the checkbox group is disabled | |
invalid | booleanIf `true`, the checkbox group is invalid | |
name | stringThe name of the input fields in the checkbox group (Useful for form submission). | |
onValueChange | (value: string[]) => voidThe callback to call when the value changes | |
readOnly | booleanIf `true`, the checkbox group is read-only | |
value | string[]The controlled value of the checkbox group |
GroupProvider
| Prop | Default | Type |
|---|---|---|
value | UseCheckboxGroupContext | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
HiddenInput
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Indicator
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
indeterminate | boolean |
| Data Attribute | Value |
|---|---|
[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
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[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
| Prop | Default | Type |
|---|---|---|
value | UseCheckboxReturn | |
asChild | booleanUse 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
| Property | Type |
|---|---|
checked | booleanWhether the checkbox is checked |
disabled | booleanWhether the checkbox is disabled |
indeterminate | booleanWhether the checkbox is indeterminate |
focused | booleanWhether the checkbox is focused |
checkedState | CheckedStateThe checked state of the checkbox |
setChecked | (checked: CheckedState) => voidFunction to set the checked state of the checkbox |
toggleChecked | VoidFunctionFunction to toggle the checked state of the checkbox |
Accessibility
Complies with the Checkbox WAI-ARIA design pattern.
Keyboard Support
| Key | Description |
|---|---|
Space | Toggle the checkbox |