CheckboxGroup

A checkbox group component for managing multiple checkbox selections

Usage

<script setup lang="ts">
import { CheckboxGroup } from '@hareui/vue'
</script>
Select your interestsChoose all that apply
Love building software
Enjoy creating beautiful interfaces
Passionate about content creation
<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 keydata-slotElement
basecheckbox-grouproot element
itemcheckboxper-item root (<div>)
contentcheckbox-contentthe clickable control + label
controlcheckbox-controlthe box
indicatorcheckbox-indicatorcheckmark / #indicator content
—labelLabel (iff label / #label)
—descriptionDescription (group and per-item)
—field-errorFieldError (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 string

Examples

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.

Select your interestsChoose all that apply
Love building software
Enjoy creating beautiful interfaces
Passionate about content creation
<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

FeaturesFeature selection is temporarily disabled
This feature is coming soon
This feature is coming soon
<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

Your skills

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.

Preferences
<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

Notification add-ons
<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

FeaturesSelect the features you want
Receive updates via email
Get weekly newsletters
<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

Select your interestsChoose all that apply
Love building software
Enjoy creating beautiful interfaces
Passionate about content creation
<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

Notification channelsChoose how we should reach you for account updates.
<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 gets mt-4
  • Items use the Checkbox theme (content, control, indicator)

API Reference

Props

PropTypeDefaultDescription
itemsCheckboxGroupItem[][]The checkbox items.
defaultValuestring[][]The values selected initially when uncontrolled.
labelstring-The group label.
descriptionstring-Help text rendered below the group label.
errorMessagestring-Error message rendered below the items while invalid. Defaults to the validation errors.
variant"primary" | "secondary"'primary'Visual style of the checkboxes.
disabledbooleanfalseWhether the whole group is disabled.
invalidbooleanundefinedWhether the group is invalid. Overrides validation when set.
readonlybooleanfalseWhether the group is read only: items stay focusable and announced, but the selection can't change.
requiredbooleanfalseWhether at least one item must be selected before form submission.
validateValidateFn<string[]>-Validates the selected values. Return an error message (or several) when invalid.
validationBehaviorValidationBehavior'native'native blocks form submission and shows errors on change or submit; aria shows errors in realtime. Defaults to the surrounding Form.
namestring-The name of the group, used when submitting an HTML form.
idstring-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.
modelValuestring[]-The selected values. Bind with v-model.

Slots

SlotPropsDescription
labelanyThe group label content. Replaces the label prop.
descriptionanyThe group description content. Replaces the description prop.
errorValidationResultThe 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

EventPayloadDescription
update:modelValue[value: string[] | undefined]-

On this page

No Headings