Checkbox
Checkboxes allow users to select multiple items from a list, or mark one individual item as selected.
Usage
<script setup lang="ts">
import { Checkbox } from '@hareui/vue'
</script><script setup lang="ts">import { Checkbox } from '@hareui/vue'</script><template> <Checkbox name="basic-terms" label="Accept terms and conditions" /></template>Anatomy
Checkbox renders a Reka UI CheckboxRoot, a control, an indicator and optional Description / FieldError — composed through props or slots.
<template>
<Checkbox label="…" description="…" error-message="…" :invalid="…">
<template #label="{ state }" /> <!-- replaces the label text -->
<template #description /> <!-- Description content -->
<template #error /> <!-- FieldError content, rendered when invalid -->
<template #indicator="{ state }" /> <!-- replaces the default checkmark / dash icons -->
</Checkbox>
</template>ui key | data-slot | Element |
|---|---|---|
base | checkbox | <div> root |
content | checkbox-content | Reka CheckboxRoot (clickable label + control) |
control | checkbox-control | Control box |
indicator | checkbox-indicator | Checkmark / dash indicator |
| — | description | Description (iff description / #description) |
| — | field-error | FieldError (while invalid, iff error-message / #error / validation errors) |
Examples
Variants
The Checkbox component supports two visual variants:
primary(default) - Standard styling with default background, suitable for most use casessecondary- Lower emphasis variant, suitable for use on surface components
Primary variant
Standard styling with default background
Secondary variant
Lower emphasis variant for use in surfaces
<script setup lang="ts">import { Checkbox } from '@hareui/vue'</script><template> <div class="flex flex-col gap-4"> <div class="flex flex-col gap-2"> <p class="text-sm font-medium text-muted">Primary variant</p> <Checkbox id="primary" name="primary" variant="primary" label="Primary checkbox" description="Standard styling with default background" /> </div> <div class="flex flex-col gap-2"> <p class="text-sm font-medium text-muted">Secondary variant</p> <Checkbox id="secondary" name="secondary" variant="secondary" label="Secondary checkbox" description="Lower emphasis variant for use in surfaces" /> </div> </div></template>Full Rounded
<script setup lang="ts">import { Checkbox, Label } from '@hareui/vue'</script><template> <div class="flex flex-col gap-6"> <div class="flex flex-col gap-3"> <Label class="text-muted">Rounded checkboxes</Label> <Checkbox name="small-rounded" label="Small size" class="[&_[data-slot='checkbox-default-indicator--checkmark']]:size-2" :ui="{ control: 'size-3 rounded-full before:rounded-full' }" /> </div> <div class="flex flex-col gap-3"> <Checkbox name="default-rounded" label="Default size" :ui="{ control: 'size-4 rounded-full before:rounded-full' }" /> </div> <div class="flex flex-col gap-3"> <Checkbox name="large-rounded" label="Large size" :ui="{ control: 'size-5 rounded-full before:rounded-full' }" /> </div> <div class="flex flex-col gap-3"> <Checkbox name="xl-rounded" label="Extra large size" class="[&_[data-slot='checkbox-default-indicator--checkmark']]:size-4" :ui="{ control: 'size-6 rounded-full before:rounded-full' }" /> </div> </div></template>Disabled
This feature is coming soon
<script setup lang="ts">import { Checkbox } from '@hareui/vue'</script><template> <Checkbox disabled id="feature" label="Premium Feature" description="This feature is coming soon" /></template>External Label
<script setup lang="ts">import { Checkbox, Label } from '@hareui/vue'</script><template> <div class="flex items-center gap-3"> <Checkbox id="label-marketing" /> <Label for="label-marketing">Send me marketing emails</Label> </div></template>With Description
Get notified when someone mentions you in a comment
<script setup lang="ts">import { Checkbox } from '@hareui/vue'</script><template> <Checkbox name="description-notifications" label="Email notifications" description="Get notified when someone mentions you in a comment" /></template>Default Selected
<script setup lang="ts">import { Checkbox } from '@hareui/vue'</script><template> <Checkbox default-value id="default-notifications" label="Enable email notifications" /></template>Invalid
You must accept the terms to continue
<script setup lang="ts">import { Checkbox } from '@hareui/vue'</script><template> <Checkbox invalid required name="agreement" label="I agree to the terms" error-message="You must accept the terms to continue" /></template>Controlled
Status: Enabled
<script setup lang="ts">import { Checkbox } from '@hareui/vue'import { ref } from 'vue'const isSelected = ref(true)</script><template> <div class="flex flex-col gap-3"> <Checkbox id="email-notifications" v-model="isSelected" label="Email notifications" /> <p class="text-sm text-muted"> Status: <span class="font-medium">{{ isSelected ? 'Enabled' : 'Disabled' }}</span> </p> </div></template>Indeterminate
Shows indeterminate state (dash icon)
<script setup lang="ts">import { Checkbox } from '@hareui/vue'import { ref } from 'vue'const checked = ref<boolean | 'indeterminate'>('indeterminate')</script><template> <Checkbox id="select-all" v-model="checked" label="Select all" description="Shows indeterminate state (dash icon)" /></template>Form Integration
<script setup lang="ts">import { Button, Checkbox } from '@hareui/vue'function handleSubmit(e: Event) { e.preventDefault() const formData = new FormData(e.target as HTMLFormElement) alert( `Form submitted with:\n${Array.from(formData.entries()) .map(([key, value]) => `${key}: ${value}`) .join('\n')}`, )}</script><template> <form class="flex flex-col gap-4" @submit="handleSubmit"> <div class="flex flex-col gap-3"> <Checkbox name="notifications" value="on" label="Enable notifications" /> <Checkbox default-value name="newsletter" value="on" label="Subscribe to newsletter" /> <Checkbox name="marketing" value="on" label="Receive marketing updates" /> </div> <Button class="mt-4" size="sm" type="submit" variant="primary"> Submit </Button> </form></template>Render Props
The #label, #description and #indicator slots receive { state } ('checked' | 'unchecked' | 'indeterminate'):
Please read and accept the terms
<script setup lang="ts">import { Checkbox } from '@hareui/vue'</script><template> <Checkbox id="render-props-terms"> <template #label="{ state }"> {{ state === 'checked' ? 'Terms accepted' : 'Accept terms' }} </template> <template #description="{ state }"> {{ state === 'checked' ? 'Thank you for accepting' : 'Please read and accept the terms' }} </template> </Checkbox></template>Custom Indicator
<script setup lang="ts">import { Checkbox } from '@hareui/vue'</script><template> <div class="flex gap-4"> <Checkbox default-value name="heart" label="Heart"> <template #indicator="{ state }"> <svg v-if="state === 'checked'" fill="currentColor" viewBox="0 0 24 24"> <path d="M12.62 20.81c-.34.12-.9.12-1.24 0C8.48 19.82 2 15.69 2 8.69 2 5.6 4.49 3.1 7.56 3.1c1.82 0 3.43.88 4.44 2.24a5.53 5.53 0 0 1 4.44-2.24C19.51 3.1 22 5.6 22 8.69c0 7-6.48 11.13-9.38 12.12Z" fill="currentColor" /> </svg> </template> </Checkbox> <Checkbox default-value name="plus" label="Plus"> <template #indicator="{ state }"> <svg v-if="state === 'checked'" fill="none" viewBox="0 0 24 24"> <path d="M6 12H18" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="3" /> <path d="M12 18V6" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="3" /> </svg> </template> </Checkbox> <Checkbox default-value="indeterminate" name="indeterminate" label="Indeterminate"> <template #indicator="{ state }"> <svg v-if="state === 'indeterminate'" stroke="currentColor" stroke-width="3" viewBox="0 0 24 24"> <line x1="21" x2="3" y1="12" y2="12" /> </svg> </template> </Checkbox> </div></template>Customization
Tailwind CSS
<script setup lang="ts">import { Checkbox } from '@hareui/vue'</script><template> <Checkbox id="custom" label="Custom styled checkbox" :ui="{ control: 'bg-success-soft before:bg-success', indicator: '**:data-[slot=checkbox-default-indicator--checkmark]:text-success-foreground', }" /></template>Global Configuration
app.use(createHareUI({
ui: { checkbox: { slots: { control: 'rounded-full' } } },
}))Styling Reference
Slots
base→[data-slot="checkbox"]– root containercontent→[data-slot="checkbox-content"]– clickable label wrapping the control and label textcontrol→[data-slot="checkbox-control"]– control boxindicator→[data-slot="checkbox-indicator"]– checkmark / indeterminate dash
Note: The optional Description and FieldError children have their own themes. See their respective pages for customization options.
Interactive States
Checkbox sets these data attributes on its root:
- Selected:
[data-selected="true"] - Indeterminate:
[data-indeterminate="true"] - Invalid:
[data-invalid="true"] - Disabled:
[data-disabled="true"]- also applies a disabled style to the description/error text - Read only:
[data-readonly="true"]on the root,aria-readonly="true"on the control button - Required:
[data-required="true"] - Hover / Focus / Active: standard pseudo-classes apply on
[data-slot="checkbox-control"]
API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue | boolean | "indeterminate" | undefined | The checked state (true, false or 'indeterminate'). Bind with v-model. |
defaultValue | boolean | "indeterminate" | false | The initial checked state when uncontrolled. |
label | string | - | The label rendered next to the control. |
description | string | - | Help text rendered below the label. |
errorMessage | string | - | Error message rendered below the label while invalid. Defaults to the validation errors. |
variant | "primary" | "secondary" | 'primary' | Visual style of the checkbox. |
disabled | boolean | false | Whether the checkbox is disabled. |
invalid | boolean | undefined | Whether the checkbox is invalid. Overrides validation when set. |
readonly | boolean | false | Whether the checkbox is read only: focusable and announced, but its state can't be changed. |
required | boolean | false | Whether user input is required on the checkbox before form submission. |
validate | ValidateFn<boolean> | - | Validates the selection (true when checked). Return an error message (or several) when invalid. |
validationBehavior | ValidationBehavior | 'native' | native blocks form submission and shows errors on change or submit; aria shows errors in realtime.
Defaults to the surrounding Form. |
name | string | - | The name of the checkbox, used when submitting an HTML form. |
value | string | 'on' | The value of the checkbox, used when submitting an HTML form. |
id | string | - | The id of the checkbox control. Generated when omitted; use it to point an external Label at the checkbox. |
ui | ComponentSlots<{ slots: { base: string[]; content: string[]; control: string[]; indicator: string[]; }; variants: { variant: { primary: { base: string; }; secondary: { control: string[]; }; }; }; defaultVariants: { variant: string; }; }> | - | Per-slot class overrides. |
Slots
| Slot | Props | Description |
|---|---|---|
label | { state: CheckboxState; } | The label content. Replaces the label prop. |
description | { state: CheckboxState; } | The description content. Replaces the description prop. |
error | ValidationResult | The error content, rendered while invalid. Replaces the errorMessage prop. |
indicator | { state: CheckboxState; } | Custom indicator inside the control. Replaces the default checkmark / dash icons. |
Emits
| Event | Payload | Description |
|---|---|---|
update:modelValue | [value: boolean | "indeterminate"] | - |
Accessibility
- The clickable label (
[data-slot="checkbox-content"]) is a native<button role="checkbox">rendered by Reka UI, keyboard-operable with Space invalidsetsaria-invalid="true";aria-describedbylists the description (<id>-description) and error (<id>-error) idsrequiredanddisabledset the matching native/ARIA attributes- For a checkbox without a visible label, pass an
aria-labelvia$attrs







