checkbox
A checkbox is a box, the text beside it, and sometimes a second quieter line under that text. All three are one component, because a label rendered separately has to be wired back to the box by hand and usually is not.
<Checkbox label="Remember this device" />
<Checkbox label="Weekly digest" description="One email on Monday." color="success" />
<Checkbox label="Remember this device" />
<Checkbox label="Weekly digest" description="One email on Monday." color="success" />
The whole thing is one label element, so clicking the text toggles the box without any
for attribute to keep in sync.
The three states
checked is true, false, or "indeterminate" — a third state, not a style. It is
what a parent checkbox shows when some but not all of its children are ticked, and the
component draws it as a filled box with a dash instead of a tick.
None of the three is a recipe variant. Ark writes data-state on the control and the
recipe styles itself off that attribute, so a resolved class string covers all three and
toggling a box never re-resolves it.
React takes checked with onCheckedChange, or defaultChecked to leave the state
alone. Vue takes v-model:checked, with defaultChecked as the uncontrolled
counterpart.
const [granted, setGranted] = useState<string[]>([]);
<Checkbox
label="All scopes"
checked={granted.length === scopes.length ? true : granted.length > 0 ? "indeterminate" : false}
onCheckedChange={(details) => setGranted(details.checked === true ? [...scopes] : [])}
/>;
<script setup lang="ts">
const accepted = ref(false);
</script>
<template>
<Checkbox v-model:checked="accepted" label="I accept the terms" />
</template>
Alignment
The box sits in a container slot whose height is the label’s own line height, so it
lines up with the first line of text rather than with the top of the block. That is what
keeps a checkbox with a two-line description looking right, and it is why the size
variant sets a height there as well as a box size.
Icons
icon replaces the tick and indeterminateIcon replaces the dash. In Vue the icon,
indeterminateIcon, label and description slots take arbitrary markup and win over
the props of the same name.
Forms
The component renders a hidden input, so a checkbox inside a form submits like a
native one. name names it, value is what gets submitted when it is checked, and
required and form do what they do on any input. readOnly shows the state without
letting anyone change it, which is different from disabled: a read-only checkbox still
takes focus.
Slots
The same word names the recipe slot, the data-slot attribute and the key in ui.
- base
- container
- control
- indicator
- wrapper
- label
- description
Variants
Read off the recipe, so these are the values that actually resolve. A Theme layer can change which one is the default; it cannot add a value.
- color
- primarysecondarysuccessinfowarningerrorneutral
- size
- xssmmdlgxl
Props
Taken by both adapters, read out of the component module. The same names and the same types work in React and Vue.
- ui
CheckboxUI
Per-slot class overrides.
- color
"primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral"
Defaults to
"primary"- size
"xs" | "sm" | "md" | "lg" | "xl"
Defaults to
"md"- label
string
Text beside the box. Clicking it toggles the checkbox.
- description
string
A quieter second line under the label.
- icon
ReactNode
Replaces the tick shown while checked.
- indeterminateIcon
ReactNode
Replaces the dash shown while indeterminate.
- disabled
boolean
- readOnly
boolean
- invalid
boolean
- required
boolean
- name
string
Submits the checkbox under this name inside a form.
- value
string
The value submitted when checked.
Defaults to
"on"- form
string
Id of the form to submit with, for a checkbox rendered outside it.
React only
Also takes Omit<React.LabelHTMLAttributes<HTMLLabelElement>, "color" | "defaultChecked" | "form">, and Pick<CheckboxRootProps, "checked" | "defaultChecked" | "onCheckedChange" | "ids">. A className among them reaches the base slot.