CheckboxGroup
A checkbox group component for managing multiple checkbox selections
Usage
<script setup lang="ts">
import { CheckboxGroup } from '@hareui/vue'
</script><script setup lang="ts">import type { CheckboxGroupItem } from '@hareui/vue'import { CheckboxGroup } from '@hareui/vue'const items: CheckboxGroupItem[] = [ { value: 'coding', label: 'Coding', description: 'Love building software' }, { value: 'design', label: 'Design', description: 'Enjoy creating beautiful interfaces' }, { value: 'writing', label: 'Writing', description: 'Passionate about content creation' },]</script><template> <CheckboxGroup name="interests" label="Select your interests" description="Choose all that apply" :items="items" /></template>Anatomy
CheckboxGroup renders its checkboxes from the items array. Label, description, error and per-item parts are composed through props or named slots.
<CheckboxGroup :items="items" label="…" description="…" error-message="…">
<template #label /> <!-- Group label -->
<template #description /> <!-- Group description -->
<template #item-label="{ item, index }" /> <!-- Each item's label -->
<template #item-description="{ item }" /> <!-- Each item's help text -->
<template #indicator="{ item, checked }" /> <!-- Custom indicator inside each control -->
<template #error="{ validationErrors }" /> <!-- FieldError content, rendered while invalid -->
</CheckboxGroup>ui key | data-slot | Element |
|---|---|---|
base | checkbox-group | root element |
item | checkbox | per-item root (<div>) |
content | checkbox-content | the clickable control + label |
control | checkbox-control | the box |
indicator | checkbox-indicator | checkmark / #indicator content |
| — | label | Label (iff label / #label) |
| — | description | Description (group and per-item) |
| — | field-error | FieldError (while invalid, iff error-message / #error / validation errors) |
Typing items
items takes the exported CheckboxGroupItem type. Extra fields are kept: extend the type, and the slots receive your type, so no casts are needed.
import type { CheckboxGroupItem } from '@hareui/vue'
interface MyItem extends CheckboxGroupItem {
icon: string
}
const items: MyItem[] = [/* ... */]
// <template #item-label="{ item }"> — item.icon is a stringExamples
In Surface
When used inside a surface (here a bg-surface container), use variant="secondary" to apply the lower emphasis variant suitable for surface backgrounds.
<script setup lang="ts">import type { CheckboxGroupItem } from '@hareui/vue'import { CheckboxGroup, Surface } from '@hareui/vue'const items: CheckboxGroupItem[] = [ { value: 'coding', label: 'Coding', description: 'Love building software' }, { value: 'design', label: 'Design', description: 'Enjoy creating beautiful interfaces' }, { value: 'writing', label: 'Writing', description: 'Passionate about content creation' },]</script><template> <Surface class="w-full rounded-3xl p-6"> <CheckboxGroup name="interests" variant="secondary" label="Select your interests" description="Choose all that apply" :items="items" /> </Surface></template>Disabled
<script setup lang="ts">import type { CheckboxGroupItem } from '@hareui/vue'import { CheckboxGroup } from '@hareui/vue'const items: CheckboxGroupItem[] = [ { value: 'feature1', label: 'Feature 1', description: 'This feature is coming soon' }, { value: 'feature2', label: 'Feature 2', description: 'This feature is coming soon' },]</script><template> <CheckboxGroup disabled name="disabled-features" label="Features" description="Feature selection is temporarily disabled" :items="items" /></template>Indeterminate
<script setup lang="ts">import type { CheckboxGroupItem } from '@hareui/vue'import { Checkbox, CheckboxGroup } from '@hareui/vue'import { computed, ref } from 'vue'const selected = ref<string[]>(['coding'])const allOptions = ['coding', 'design', 'writing']const indeterminate = computed(() => selected.value.length > 0 && selected.value.length < allOptions.length)const allSelected = computed(() => selected.value.length === allOptions.length)function onToggleAll(isSelected: boolean | 'indeterminate') { selected.value = isSelected === true ? [...allOptions] : []}const items: CheckboxGroupItem[] = [ { value: 'coding', label: 'Coding' }, { value: 'design', label: 'Design' }, { value: 'writing', label: 'Writing' },]</script><template> <div> <Checkbox name="select-all" label="Select all" :model-value="indeterminate ? 'indeterminate' : allSelected" @update:model-value="onToggleAll" /> <div class="ms-6 flex flex-col gap-2"> <CheckboxGroup v-model="selected" :items="items" /> </div> </div></template>Controlled
Selected: coding, design
<script setup lang="ts">import type { CheckboxGroupItem } from '@hareui/vue'import { CheckboxGroup } from '@hareui/vue'import { ref } from 'vue'const selected = ref<string[]>(['coding', 'design'])const items: CheckboxGroupItem[] = [ { value: 'coding', label: 'Coding' }, { value: 'design', label: 'Design' }, { value: 'writing', label: 'Writing' },]</script><template> <div class="flex min-w-[320px] flex-col gap-3"> <CheckboxGroup v-model="selected" name="skills" label="Your skills" :items="items" /> <p class="my-4 text-sm text-muted"> Selected: {{ selected.join(', ') || 'None' }} </p> </div></template>Validation
Mark the group required inside a Form: submitting with nothing selected is blocked and shows the error. A validate function receives the selected values, and invalid overrides both.
<script setup lang="ts">import type { CheckboxGroupItem } from '@hareui/vue'import { Button, CheckboxGroup, Form } from '@hareui/vue'import { ref } from 'vue'const message = ref<string | null>(null)const items: CheckboxGroupItem[] = [ { value: 'email', label: 'Email notifications' }, { value: 'sms', label: 'SMS notifications' }, { value: 'push', label: 'Push notifications' },]function onSubmit(e: Event) { e.preventDefault() const formData = new FormData(e.currentTarget as HTMLFormElement) const values = formData.getAll('preferences') message.value = `Selected preferences: ${values.join(', ')}`}</script><template> <Form class="flex flex-col gap-4" @submit="onSubmit"> <CheckboxGroup required name="preferences" label="Preferences" error-message="Please select at least one notification method." :items="items" /> <Button class="mt-2 w-fit" type="submit"> Submit </Button> <p v-if="!!message" class="text-sm text-muted"> {{ message }} </p> </Form></template>Features and Add-ons Example
<script setup lang="ts">import type { CheckboxGroupItem } from '@hareui/vue'import { CheckboxGroup } from '@hareui/vue'import { Icon } from '@iconify/vue'const contentClass = 'w-full items-start gap-3 rounded-xl border border-border/50 bg-default px-4 py-3 transition-colors hover:bg-default-hover data-[selected=true]:border-primary/30 data-[selected=true]:bg-primary-soft'const controlClass = 'mt-0.5'interface AddOn extends CheckboxGroupItem { icon: string desc: string}const addOns: AddOn[] = [ { icon: 'gravity-ui:envelope', label: 'Email alerts', desc: 'Get notified by email about important changes', value: 'email-alerts' }, { icon: 'gravity-ui:comment', label: 'Comment mentions', desc: 'Receive a notification when someone mentions you', value: 'comment-mentions' }, { icon: 'gravity-ui:bell', label: 'Push notifications', desc: 'Get push notifications on your devices', value: 'push-notifications' },]</script><template> <CheckboxGroup name="addons" label="Notification add-ons" class="w-full max-w-sm gap-2 **:data-[slot=checkbox]:mt-0" :default-value="['email-alerts']" :items="addOns" :ui="{ content: contentClass, control: controlClass }" > <template #item-label="{ item }"> <div class="flex flex-1 items-start gap-3"> <Icon :icon="item.icon" class="mt-0.5 size-5 shrink-0 text-muted" /> <div class="flex flex-col gap-0.5"> <span class="font-medium text-foreground">{{ item.label }}</span> <span class="text-sm text-muted">{{ item.desc }}</span> </div> </div> </template> </CheckboxGroup></template>With Custom Indicator
<script setup lang="ts">import type { CheckboxGroupItem } from '@hareui/vue'import { CheckboxGroup } from '@hareui/vue'const items: CheckboxGroupItem[] = [ { value: 'notifications', label: 'Email notifications', description: 'Receive updates via email' }, { value: 'newsletter', label: 'Newsletter', description: 'Get weekly newsletters' },]</script><template> <CheckboxGroup name="features" label="Features" description="Select the features you want" :items="items"> <template #indicator="{ checked }"> <svg v-if="checked" aria-hidden="true" fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="2" viewBox="0 0 24 24"> <path d="M6 18L18 6M6 6l12 12" /> </svg> </template> </CheckboxGroup></template>Render Function
<script setup lang="ts">import type { CheckboxGroupItem } from '@hareui/vue'import { CheckboxGroup } from '@hareui/vue'const items: CheckboxGroupItem[] = [ { value: 'coding', label: 'Coding', description: 'Love building software' }, { value: 'design', label: 'Design', description: 'Enjoy creating beautiful interfaces' }, { value: 'writing', label: 'Writing', description: 'Passionate about content creation' },]</script><template> <!-- Attributes fall through to the root element (HeroUI's `render` prop) --> <CheckboxGroup name="interests" data-custom="foo" label="Select your interests" description="Choose all that apply" :items="items" /></template>Attributes such as data-* fall through to the root element.
Customization
Tailwind CSS
<script setup lang="ts">import type { CheckboxGroupItem } from '@hareui/vue'import { CheckboxGroup } from '@hareui/vue'const controlClass = 'bg-success-soft before:bg-success'const indicatorClass = '**:data-[slot=checkbox-default-indicator--checkmark]:text-success-foreground'const channels: CheckboxGroupItem[] = [ { label: 'Email', value: 'email' }, { label: 'SMS', value: 'sms' }, { label: 'Push', value: 'push' },]</script><template> <CheckboxGroup name="notification-channels" label="Notification channels" description="Choose how we should reach you for account updates." class="gap-3 **:data-[slot=checkbox]:mt-0" :default-value="['email']" :items="channels" :ui="{ control: controlClass, indicator: indicatorClass }" /></template>Global Configuration
app.use(createHareUI({
ui: { checkboxGroup: { slots: { base: 'flex flex-col gap-2' } } },
}))Styling Reference
Slots
base→[data-slot="checkbox-group"]—flex flex-col; each item after the first getsmt-4- Items use the Checkbox theme (
content,control,indicator)
API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | CheckboxGroupItem[] | [] | The checkbox items. |
defaultValue | string[] | [] | The values selected initially when uncontrolled. |
label | string | - | The group label. |
description | string | - | Help text rendered below the group label. |
errorMessage | string | - | Error message rendered below the items while invalid. Defaults to the validation errors. |
variant | "primary" | "secondary" | 'primary' | Visual style of the checkboxes. |
disabled | boolean | false | Whether the whole group is disabled. |
invalid | boolean | undefined | Whether the group is invalid. Overrides validation when set. |
readonly | boolean | false | Whether the group is read only: items stay focusable and announced, but the selection can't change. |
required | boolean | false | Whether at least one item must be selected before form submission. |
validate | ValidateFn<string[]> | - | Validates the selected values. 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 group, used when submitting an HTML form. |
id | string | - | Base id of the group; item controls get ${id}-${index}. Generated when omitted. |
ui | (ComponentSlots<{ slots: { base: string[]; }; variants: { variant: { primary: { base: string; }; secondary: { base: string; }; }; }; defaultVariants: { variant: string; }; }> & Pick<ComponentSlots<{ slots: { base: string[]; content: string[]; control: string[]; indicator: string[]; }; variants: { variant: { primary: { base: string; }; secondary: { control: string[]; }; }; }; defaultVariants: { variant: string; }; }>, "indicator" | "content" | "control"> & { item?: any; }) | - | Per-slot class overrides. item is each checkbox's root; content, control and indicator are the checkbox theme's. |
modelValue | string[] | - | The selected values. Bind with v-model. |
Slots
| Slot | Props | Description |
|---|---|---|
label | any | The group label content. Replaces the label prop. |
description | any | The group description content. Replaces the description prop. |
error | ValidationResult | The error content, rendered while invalid. Replaces the errorMessage prop. |
item-label | { item: CheckboxGroupItem; index: number; } | The label of each item. Replaces item.label. |
item-description | { item: CheckboxGroupItem; index: number; } | The description of each item. Replaces item.description. |
indicator | { item: CheckboxGroupItem; checked: boolean; } | Custom content inside each control. Replaces the default checkmark. |
Emits
| Event | Payload | Description |
|---|---|---|
update:modelValue | [value: string[] | undefined] | - |





