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 keydata-slotElement
basecheckbox<div> root
contentcheckbox-contentReka CheckboxRoot (clickable label + control)
controlcheckbox-controlControl box
indicatorcheckbox-indicatorCheckmark / dash indicator
—descriptionDescription (iff description / #description)
—field-errorFieldError (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 cases
  • secondary - 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

<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 container
  • content → [data-slot="checkbox-content"] – clickable label wrapping the control and label text
  • control → [data-slot="checkbox-control"] – control box
  • indicator → [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

PropTypeDefaultDescription
modelValueboolean | "indeterminate"undefinedThe checked state (true, false or 'indeterminate'). Bind with v-model.
defaultValueboolean | "indeterminate"falseThe initial checked state when uncontrolled.
labelstring-The label rendered next to the control.
descriptionstring-Help text rendered below the label.
errorMessagestring-Error message rendered below the label while invalid. Defaults to the validation errors.
variant"primary" | "secondary"'primary'Visual style of the checkbox.
disabledbooleanfalseWhether the checkbox is disabled.
invalidbooleanundefinedWhether the checkbox is invalid. Overrides validation when set.
readonlybooleanfalseWhether the checkbox is read only: focusable and announced, but its state can't be changed.
requiredbooleanfalseWhether user input is required on the checkbox before form submission.
validateValidateFn<boolean>-Validates the selection (true when checked). 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 checkbox, used when submitting an HTML form.
valuestring'on'The value of the checkbox, used when submitting an HTML form.
idstring-The id of the checkbox control. Generated when omitted; use it to point an external Label at the checkbox.
uiComponentSlots<{ 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

SlotPropsDescription
label{ state: CheckboxState; }The label content. Replaces the label prop.
description{ state: CheckboxState; }The description content. Replaces the description prop.
errorValidationResultThe error content, rendered while invalid. Replaces the errorMessage prop.
indicator{ state: CheckboxState; }Custom indicator inside the control. Replaces the default checkmark / dash icons.

Emits

EventPayloadDescription
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
  • invalid sets aria-invalid="true"; aria-describedby lists the description (<id>-description) and error (<id>-error) ids
  • required and disabled set the matching native/ARIA attributes
  • For a checkbox without a visible label, pass an aria-label via $attrs

On this page

No Headings