Skip to content

FormCheckbox

Boolean checkbox built on Reka UI's Checkbox primitive. <VCFormCheckbox> handles the single-value case (v-model: boolean | 'indeterminate'); wrap multiple checkboxes in <VCFormCheckboxGroup> for an array v-model.

For toggle-switch UI, use the dedicated <VCFormSwitch> component instead — Reka splits Checkbox and Switch into separate primitives for a reason (different ARIA semantics).

bash
npm install @vuecs/forms

Basic usage

vue
<script setup lang="ts">
import { VCFormCheckbox, VCFormCheckboxGroup } from '@vuecs/forms';
import { ref } from 'vue';

const accepted = ref(false);
const indeterminateState = ref<boolean | 'indeterminate'>('indeterminate');
const selected = ref<string[]>(['a']);
</script>

<template>
    <VCFormCheckbox v-model="accepted" label-content="I accept the terms" />

    <VCFormCheckbox
        v-model="indeterminateState"
        label-content="Indeterminate (tri-state)"
    />

    <VCFormCheckboxGroup v-model="selected">
        <VCFormCheckbox value="a" label-content="Apples" />
        <VCFormCheckbox value="b" label-content="Bananas" />
        <VCFormCheckbox value="c" label-content="Cherries" />
    </VCFormCheckboxGroup>
</template>
css
/* Tailwind v4 + design tokens */
@import "tailwindcss";
@import "@vuecs/design";

/*
 * Structural CSS for the checkbox indicator + group container ships in
 * @vuecs/forms. Without this import the checked-state checkmark + group
 * layout helpers are missing.
 */
@import "@vuecs/forms";

/* Class-based dark mode (optional — pairs with the tokens) */
@custom-variant dark (&:where(.dark, .dark *));

Custom label markup

Use the named #label slot to render the label as markup. The slot receives class (the resolved label class) and id (matching the checkbox's for):

vue
<VCFormCheckbox v-model="accepted">
    <template #label="{ class: labelClass, id }">
        <label :class="labelClass" :for="id">
            I accept the <a href="/terms">terms</a>
        </label>
    </template>
</VCFormCheckbox>

Default glyph (checkmark / indeterminate dash)

A checked box renders a check mark, and 'indeterminate' renders a dash, out of the box — both ship as currentColor-painted CSS masks in the structural stylesheet, so no icon package or slot wiring is needed.

Structural stylesheet required

The glyph lives in @vuecs/forms' structural CSS, not in the theme class strings. If a checked checkbox shows a bare color fill with no check mark, the stylesheet import is missing:

css
@import "@vuecs/forms"; /* → dist/style.css */

The same import carries the switch track/thumb structure, slider rails, and the other form structural rules — see Installation.

Custom indicator (checkmark)

Replace the default checkmark via the #indicator slot:

vue
<VCFormCheckbox v-model="accepted">
    <template #indicator="{ class: indicatorClass }">
        <span :class="indicatorClass">✔</span>
    </template>
</VCFormCheckbox>

Group v-model

<VCFormCheckboxGroup> wraps Reka's CheckboxGroupRoot. Each child <VCFormCheckbox value="..."> automatically syncs with the group's array v-model; you don't need to thread update:modelValue manually.

vue
<VCFormCheckboxGroup v-model="selected" orientation="horizontal">
    <VCFormCheckbox value="a" label-content="A" />
    <VCFormCheckbox value="b" label-content="B" />
</VCFormCheckboxGroup>

The group also enables roving focus (arrow keys to move between children) by default.

Behavioral defaults

KeyDefaultDescription
labelContent'Input'Label text when neither label-content prop nor #label slot is provided

Props (<VCFormCheckbox>)

PropTypeDefaultDescription
modelValueboolean | 'indeterminate' | nullundefinedChecked state. Pass 'indeterminate' for tri-state.
valueunknown'on'Value pushed onto the parent <VCFormCheckboxGroup>'s array v-model
disabledbooleanfalseDisables interaction; reflected as data-disabled
requiredbooleanfalseNative form-required attribute
namestringundefinedForm-submission name
idstring(auto)Override the auto-generated id
labelbooleantrueRender the label wrapper. Set false for the bare checkbox
labelContentstring(defaults system)Inline label text — bypasses the #label slot
themeClassPartial<FormCheckboxThemeClasses>undefinedPer-instance theme overrides
themeVariantRecord<string, string | boolean>undefinedPer-instance variant values

Props (<VCFormCheckboxGroup>)

PropTypeDefaultDescription
modelValueunknown[]undefinedSelected values
orientation'vertical' | 'horizontal''vertical'Layout + arrow-key direction
loopbooleantrueWrap arrow-key focus from last to first child
rovingFocusbooleantrueEnable arrow-key roving focus across children
disabledbooleanfalseDisable every child
requiredbooleanfalseForm-required attribute
namestringundefinedForm-submission name

Slots (<VCFormCheckbox>)

SlotSlot propsDescription
label{ class, id }Custom label markup. class is the resolved label theme class; id matches the checkbox's for target. Replaces the default <label>
indicator{ class }Custom checkmark content. class is the resolved indicator theme class

See also

Released under the Apache 2.0 License.