Checkbox
A control element that allows for multiple selections within a set.
You can explore the checkbox component in the following curated examples.
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 thelabel
element is the direct child of theCheckbox.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'
export const Basic = () => (
<Checkbox.Root>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
export const Basic = () => (
<Checkbox.Root>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
</script>
<template>
<Checkbox.Root>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
</script>
<Checkbox.Root>
<Checkbox.Label>Accept terms and conditions</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<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'
export const Controlled = () => {
const [checked, setChecked] = useState<Checkbox.CheckedState>(true)
return (
<Checkbox.Root checked={checked} onCheckedChange={(e) => setChecked(e.checked)}>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
}
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
export const Controlled = () => {
const [checked, setChecked] = createSignal<Checkbox.CheckedState>(true)
return (
<Checkbox.Root checked={checked()} onCheckedChange={(e) => setChecked(e.checked)}>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<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'
const checked = ref<CheckboxCheckedState>(true)
</script>
<template>
<Checkbox.Root v-model:checked="checked">
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
let checked = $state(false)
</script>
<Checkbox.Root bind:checked>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Label>Controlled 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'
export const DefaultChecked = () => (
<Checkbox.Root defaultChecked>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
export const DefaultChecked = () => (
<Checkbox.Root defaultChecked>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
</script>
<template>
<Checkbox.Root defaultChecked>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
</script>
<Checkbox.Root defaultChecked>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<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'
export const Disabled = () => (
<Checkbox.Root disabled>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
export const Disabled = () => (
<Checkbox.Root disabled>
<Checkbox.Label>Accept terms and conditions</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
</script>
<template>
<Checkbox.Root disabled>
<Checkbox.Label>Accept terms and conditions</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
</script>
<Checkbox.Root disabled>
<Checkbox.Label>Accept terms and conditions</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<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'
export const Indeterminate = () => (
<Checkbox.Root checked="indeterminate">
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon, MinusIcon } from 'lucide-solid'
export const Indeterminate = () => (
<Checkbox.Root defaultChecked="indeterminate">
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon, MinusIcon } from 'lucide-vue-next'
</script>
<template>
<Checkbox.Root checked="indeterminate">
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon, MinusIcon } from 'lucide-svelte'
</script>
<Checkbox.Root checked="indeterminate">
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<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'
export const ProgrammaticControl = () => {
const checkbox = useCheckbox()
return (
<>
<button type="button" onClick={() => checkbox.setChecked(true)}>
Check
</button>
<button type="button" onClick={() => checkbox.setChecked(false)}>
Uncheck
</button>
<Checkbox.RootProvider value={checkbox}>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.RootProvider>
</>
)
}
import { Checkbox, useCheckbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
export const ProgrammaticControl = () => {
const checkbox = useCheckbox()
return (
<>
<button type="button" onClick={() => checkbox().setChecked(true)}>
Check
</button>
<button type="button" onClick={() => checkbox().setChecked(false)}>
Uncheck
</button>
<Checkbox.RootProvider value={checkbox}>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.RootProvider>
</>
)
}
<script setup lang="ts">
import { Checkbox, useCheckbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
const checkbox = useCheckbox()
</script>
<template>
<button type="button" @click="() => checkbox.setChecked(true)">Check</button>
<button type="button" @click="() => checkbox.setChecked(false)">Uncheck</button>
<Checkbox.RootProvider :value="checkbox">
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.RootProvider>
</template>
<script lang="ts">
import { Checkbox, useCheckbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
const checkbox = useCheckbox()
</script>
<button type="button" onclick={() => checkbox().setChecked(true)}>Check</button>
<button type="button" onclick={() => checkbox().setChecked(false)}>Uncheck</button>
<Checkbox.RootProvider value={checkbox}>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.RootProvider>
Access Checkbox state
Use the Checkbox.Context
and its render prop to access the checkbox's state and methods.
import { Checkbox } from '@ark-ui/react/checkbox'
import { CheckIcon } from 'lucide-react'
export const RenderProp = () => (
<Checkbox.Root>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Context>
{(checkbox) => <Checkbox.Label>Checkbox {checkbox.checked.toString()}</Checkbox.Label>}
</Checkbox.Context>
<Checkbox.HiddenInput />
</Checkbox.Root>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
export const RenderProp = () => (
<Checkbox.Root>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Context>
{(checkbox) => <Checkbox.Label>Checkbox {checkbox().checked.toString()}</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'
</script>
<template>
<Checkbox.Root>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Context v-slot="checkbox">
<Checkbox.Label>Checkbox {{ checkbox.checked.toString() }}</Checkbox.Label>
</Checkbox.Context>
<Checkbox.HiddenInput />
</Checkbox.Root>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
</script>
<Checkbox.Root>
<Checkbox.Control>
<Checkbox.Indicator><CheckIcon /></Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Context>
{#snippet render(api)}
<Checkbox.Label>Checkbox {api().checked.toString()}</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'
export const WithField = (props: Field.RootProps) => (
<Field.Root {...props}>
<Checkbox.Root>
<Checkbox.Label>Label</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
<Field.HelperText>Additional Info</Field.HelperText>
<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'
export const WithField = (props: Field.RootProps) => (
<Field.Root {...props}>
<Checkbox.Root>
<Checkbox.Label>Label</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
<Field.HelperText>Additional Info</Field.HelperText>
<Field.ErrorText>Error Info</Field.ErrorText>
</Field.Root>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { Field, type FieldRootProps } from '@ark-ui/vue/field'
import { CheckIcon, MinusIcon } from 'lucide-vue-next'
const props = defineProps<FieldRootProps>()
</script>
<template>
<Field.Root v-bind="props">
<Checkbox.Root>
<Checkbox.Label>Label</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
<Field.HelperText>Additional Info</Field.HelperText>
<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'
</script>
<Field.Root>
<Checkbox.Root>
<Checkbox.Label>Label</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
<Field.HelperText>Additional Info</Field.HelperText>
<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'
export const WithForm = () => (
<form
onSubmit={(e) => {
e.preventDefault()
const formData = new FormData(e.currentTarget)
console.log('terms:', formData.get('terms'))
}}
>
<Checkbox.Root name="terms" value="accepted">
<Checkbox.Label>I agree to the terms and conditions</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
<button type="submit">Submit</button>
</form>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
const handleSubmit = (event: Event) => {
event.preventDefault()
const formData = new FormData(event.target as HTMLFormElement)
console.log('terms:', formData.get('terms'))
}
export const WithForm = () => (
<form onSubmit={handleSubmit}>
<Checkbox.Root name="terms" value="accepted">
<Checkbox.Label>I agree to the terms and conditions</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
<button type="submit">Submit</button>
</form>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
const handleSubmit = (event: Event) => {
event.preventDefault()
const formData = new FormData(event.target as HTMLFormElement)
console.log('terms:', formData.get('terms'))
}
</script>
<template>
<form @submit="handleSubmit">
<Checkbox.Root name="terms" value="accepted">
<Checkbox.Label>I agree to the terms and conditions</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
<button type="submit">Submit</button>
</form>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
</script>
<form
onsubmit={(e) => {
e.preventDefault()
const formData = new FormData(e.currentTarget)
console.log('terms:', formData.get('terms'))
}}
>
<Checkbox.Root name="terms" value="accepted">
<Checkbox.Label>I agree to the terms and conditions</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
<button 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'
export const RootProvider = () => {
const checkbox = useCheckbox()
return (
<>
<span>{checkbox.checked ? 'Checked' : 'UnChecked'}</span>
<Checkbox.RootProvider value={checkbox}>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.RootProvider>
</>
)
}
import { Checkbox, useCheckbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
export const RootProvider = () => {
const checkbox = useCheckbox()
return (
<>
<span>{checkbox().checked ? 'Checked' : 'UnChecked'}</span>
<Checkbox.RootProvider value={checkbox}>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.RootProvider>
</>
)
}
<script setup lang="ts">
import { Checkbox, useCheckbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
const checkbox = useCheckbox()
</script>
<template>
<span>{{ checkbox.checked ? 'Checked' : 'UnChecked' }}</span>
<Checkbox.RootProvider :value="checkbox">
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.RootProvider>
</template>
<script lang="ts">
import { Checkbox, useCheckbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
const id = $props.id()
const checkbox = useCheckbox({ id })
</script>
<span>{checkbox().checked ? 'Checked' : 'UnChecked'}</span>
<Checkbox.RootProvider value={checkbox}>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.RootProvider>
If you're using the
Checkbox.RootProvider
component, you don't need to use theCheckbox.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'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
export const Group = () => (
<Checkbox.Group defaultValue={['react']} name="framework" onValueChange={console.log}>
{items.map((item) => (
<Checkbox.Root value={item.value} key={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
))}
</Checkbox.Group>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
import { For } from 'solid-js'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
export const Group = () => (
<Checkbox.Group defaultValue={['react']} name="framework" onValueChange={console.log}>
<For each={items}>
{(item) => (
<Checkbox.Root value={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)}
</For>
</Checkbox.Group>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
</script>
<template>
<Checkbox.Group :defaultValue="['react']" name="framework" @valueChange="console.log">
<Checkbox.Root v-for="item in items" :value="item.value" :key="item.value">
<Checkbox.Label>{{ item.label }}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
</Checkbox.Group>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
</script>
<Checkbox.Group>
{#each items as item}
<Checkbox.Root value={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<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'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
export const GroupControlled = () => {
const [value, setValue] = useState(['react'])
return (
<div>
<Checkbox.Group value={value} name="framework" onValueChange={setValue}>
{items.map((item) => (
<Checkbox.Root value={item.value} key={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
))}
</Checkbox.Group>
<pre>Selected: {JSON.stringify(value)}</pre>
</div>
)
}
import { Checkbox } from '@ark-ui/solid/checkbox'
import { Check } from 'lucide-solid'
import { createSignal, For } from 'solid-js'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
export const GroupControlled = () => {
const [value, setValue] = createSignal(['react'])
const toggleValue = () => {
setValue((prev) => (prev[0] === 'react' ? ['solid'] : ['react']))
}
return (
<>
<button onClick={toggleValue}>Toggle Value</button>
<Checkbox.Group value={value} onValueChange={setValue} name="framework">
<For each={items}>
{(item) => (
<Checkbox.Root value={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<Check />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)}
</For>
</Checkbox.Group>
</>
)
}
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
import { ref } from 'vue'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
]
const value = ref(['react'])
const toggleValue = () => {
value.value = value.value[0] === 'react' ? ['solid'] : ['react']
}
</script>
<template>
<button @click="toggleValue">Toggle Value</button>
<Checkbox.Group v-model="value" name="framework">
<Checkbox.Root v-for="item in items" :value="item.value" :key="item.value">
<Checkbox.Label>{{ item.label }}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
</Checkbox.Group>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
let value = $state(['react'])
</script>
<Checkbox.Group bind:value name="framework">
{#each items as item}
<Checkbox.Root value={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
{/each}
</Checkbox.Group>
<pre>Selected: {JSON.stringify(value)}</pre>
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'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
export const GroupProvider = () => {
const group = useCheckboxGroup({
defaultValue: ['react'],
name: 'framework',
})
return (
<Checkbox.GroupProvider value={group}>
{items.map((item) => (
<Checkbox.Root value={item.value} key={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
))}
</Checkbox.GroupProvider>
)
}
import { Checkbox, useCheckboxGroup } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
import { For } from 'solid-js'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
export const GroupProvider = () => {
const group = useCheckboxGroup({
defaultValue: ['react'],
name: 'framework',
})
return (
<Checkbox.GroupProvider value={group}>
<For each={items}>
{(item) => (
<Checkbox.Root value={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)}
</For>
</Checkbox.GroupProvider>
)
}
<script setup lang="ts">
import { Checkbox, useCheckboxGroup } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
const group = useCheckboxGroup({
defaultValue: ['react'],
name: 'framework',
})
</script>
<template>
<Checkbox.GroupProvider :value="group">
<Checkbox.Root v-for="item in items" :value="item.value" :key="item.value">
<Checkbox.Label>{{ item.label }}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
</Checkbox.GroupProvider>
</template>
<script lang="ts">
import { Checkbox, useCheckboxGroup } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
const group = useCheckboxGroup({
defaultValue: ['react'],
name: 'framework',
})
</script>
<Checkbox.GroupProvider value={group}>
{#each items as item}
<Checkbox.Root value={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<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'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
export const GroupWithForm = () => (
<form
onSubmit={(e) => {
e.preventDefault()
console.log(new FormData(e.currentTarget).getAll('framework'))
}}
>
<Checkbox.Group defaultValue={['react']} name="framework">
{items.map((item) => (
<Checkbox.Root value={item.value} key={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
))}
</Checkbox.Group>
<button type="submit">Submit</button>
</form>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
import { For } from 'solid-js'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Svelte', value: 'svelte' },
{ label: 'Vue', value: 'vue' },
]
const handleSubmit = (event: Event) => {
event.preventDefault()
const formData = new FormData(event.target as HTMLFormElement)
console.log(formData.getAll('framework'))
}
export const GroupWithForm = () => (
<form onSubmit={handleSubmit}>
<Checkbox.Group defaultValue={['react']} name="framework">
<For each={items}>
{(item) => (
<Checkbox.Root value={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)}
</For>
</Checkbox.Group>
<button type="submit">Submit</button>
</form>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Svelte', value: 'svelte' },
{ label: 'Vue', value: 'vue' },
]
const handleSubmit = (event: Event) => {
event.preventDefault()
const formData = new FormData(event.target as HTMLFormElement)
console.log(formData.getAll('framework'))
}
</script>
<template>
<form @submit="handleSubmit">
<Checkbox.Group :default-value="['react']" name="framework">
<Checkbox.Root v-for="item in items" :key="item.value" :value="item.value">
<Checkbox.Label>{{ item.label }}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
</Checkbox.Group>
<button type="submit">Submit</button>
</form>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Svelte', value: 'svelte' },
{ label: 'Vue', value: 'vue' },
]
</script>
<form
onsubmit={(e) => {
e.preventDefault()
console.log(new FormData(e.currentTarget).getAll('framework'))
}}
>
<Checkbox.Group defaultValue={['react']} name="framework">
{#each items as item (item.value)}
<Checkbox.Root value={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
{/each}
</Checkbox.Group>
<button 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'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
export const GroupWithInvalid = () => (
<Checkbox.Group invalid>
{items.map((item) => (
<Checkbox.Root value={item.value} key={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
))}
</Checkbox.Group>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon } from 'lucide-solid'
import { For } from 'solid-js'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
export const GroupWithInvalid = () => (
<Checkbox.Group invalid>
<For each={items}>
{(item) => (
<Checkbox.Root value={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)}
</For>
</Checkbox.Group>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon } from 'lucide-vue-next'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
</script>
<template>
<Checkbox.Group invalid>
<Checkbox.Root v-for="item in items" :key="item.value" :value="item.value">
<Checkbox.Label>{{ item.label }}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
</Checkbox.Group>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { CheckIcon } from 'lucide-svelte'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
</script>
<Checkbox.Group invalid>
{#each items as item (item.value)}
<Checkbox.Root value={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<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'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
const CheckboxItem = (props: Checkbox.RootProps) => {
return (
<Checkbox.Root {...props}>
<Checkbox.Label>{props.children}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<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 }}>
<CheckboxItem
value="all"
checked={indeterminate ? 'indeterminate' : allSelected}
onCheckedChange={(e) => handleSelectAll(!!e.checked)}
>
Select All
</CheckboxItem>
<Checkbox.Group value={value} name="framework" onValueChange={setValue}>
{items.map((item) => (
<CheckboxItem value={item.value} key={item.value}>
{item.label}
</CheckboxItem>
))}
</Checkbox.Group>
<pre>Selected: {JSON.stringify(value)}</pre>
</div>
)
}
import { Checkbox } from '@ark-ui/solid/checkbox'
import { CheckIcon, MinusIcon } from 'lucide-solid'
import { For, createSignal, createMemo } from 'solid-js'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
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' }}>
<Checkbox.Root
checked={indeterminate() ? 'indeterminate' : allSelected()}
onCheckedChange={(details) => handleSelectAll(!!details.checked)}
>
<Checkbox.Label>Select All</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
<Checkbox.Group value={value} onValueChange={setValue} name="framework">
<For each={items}>
{(item) => (
<Checkbox.Root value={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)}
</For>
</Checkbox.Group>
<pre>Selected: {JSON.stringify(value())}</pre>
</div>
)
}
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { CheckIcon, MinusIcon } from 'lucide-vue-next'
import { computed, ref } from 'vue'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
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)
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 10px">
<Checkbox.Root
:checked="indeterminate ? 'indeterminate' : allSelected"
@checked-change="(e) => handleSelectAll(!!e.checked)"
>
<Checkbox.Label>Select All</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
<Checkbox.Group v-model="value" name="framework">
<Checkbox.Root v-for="item in items" :key="item.value" :value="item.value">
<Checkbox.Label>{{ item.label }}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<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, MinusIcon } from 'lucide-svelte'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
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 value={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
{/snippet}
<div style="display: flex; flex-direction: column; gap: 10px;">
<Checkbox.Root
checked={indeterminate ? 'indeterminate' : allSelected}
onCheckedChange={(e: { checked: boolean | 'indeterminate' }) => handleSelectAll(!!e.checked)}
>
<Checkbox.Label>Select All</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
<MinusIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
<Checkbox.Group bind:value name="framework">
{#each items as item (item.value)}
{@render CheckboxItem(item)}
{/each}
</Checkbox.Group>
<pre>Selected: {JSON.stringify(value)}</pre>
</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'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
export const GroupWithFieldset = () => (
<Fieldset.Root>
<Fieldset.Legend>Select frameworks</Fieldset.Legend>
<Checkbox.Group defaultValue={['react']} name="framework">
{items.map((item) => (
<Checkbox.Root value={item.value} key={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
))}
</Checkbox.Group>
<Fieldset.HelperText>Choose your preferred frameworks</Fieldset.HelperText>
<Fieldset.ErrorText>Error Text</Fieldset.ErrorText>
</Fieldset.Root>
)
import { Checkbox } from '@ark-ui/solid/checkbox'
import { Fieldset } from '@ark-ui/solid/fieldset'
import { CheckIcon } from 'lucide-solid'
import { Index } from 'solid-js'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
export const GroupWithFieldset = () => (
<Fieldset.Root>
<Fieldset.Legend>Select frameworks</Fieldset.Legend>
<Checkbox.Group defaultValue={['react']} name="framework">
<Index each={items}>
{(item) => (
<Checkbox.Root value={item().value}>
<Checkbox.Label>{item().label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
)}
</Index>
</Checkbox.Group>
<Fieldset.HelperText>Choose your preferred frameworks</Fieldset.HelperText>
<Fieldset.ErrorText>Error Text</Fieldset.ErrorText>
</Fieldset.Root>
)
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { Fieldset } from '@ark-ui/vue/fieldset'
import { CheckIcon } from 'lucide-vue-next'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
</script>
<template>
<Fieldset.Root>
<Fieldset.Legend>Select frameworks</Fieldset.Legend>
<Checkbox.Group :defaultValue="['react']" name="framework">
<Checkbox.Root v-for="item in items" :key="item.value" :value="item.value">
<Checkbox.Label>{{ item.label }}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
</Checkbox.Group>
<Fieldset.HelperText>Choose your preferred frameworks</Fieldset.HelperText>
<Fieldset.ErrorText>Error Text</Fieldset.ErrorText>
</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'
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]
</script>
<Fieldset.Root>
<Fieldset.Legend>Select frameworks</Fieldset.Legend>
<Checkbox.Group defaultValue={['react']} name="framework">
{#each items as item (item.value)}
<Checkbox.Root value={item.value}>
<Checkbox.Label>{item.label}</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>
<CheckIcon />
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
{/each}
</Checkbox.Group>
<Fieldset.HelperText>Choose your preferred frameworks</Fieldset.HelperText>
<Fieldset.ErrorText>Error Text</Fieldset.ErrorText>
</Fieldset.Root>
API Reference
Root
Prop | Default | Type |
---|---|---|
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 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 |
Control
Prop | Default | Type |
---|---|---|
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 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 |
Group
Prop | Default | Type |
---|---|---|
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
Prop | Default | Type |
---|---|---|
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
Prop | Default | Type |
---|---|---|
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
Prop | Default | Type |
---|---|---|
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 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 |
Label
Prop | Default | Type |
---|---|---|
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 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 |
RootProvider
Prop | Default | Type |
---|---|---|
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. |
Accessibility
Complies with the Checkbox WAI-ARIA design pattern.
Keyboard Support
Key | Description |
---|---|
Space | Toggle the checkbox |