Skip to content

Checkbox Group

Basic Usage

The CheckboxGroup field renders a group of checkboxes for selecting multiple values from a finite option list.

Example
Permissions
php
CheckboxGroup::make('permissions')->options([
    'view' => 'View entries',
    'comment' => 'Add comments',
    'approve' => 'Approve changes',
])->default(['view', 'comment']);

The field value is always an array. A missing or null model value becomes [], an existing array is preserved, a comma-separated string is split and trimmed, and any other scalar becomes a one-item array. Standalone Vue and React components also default to [].

Each option value may be a string, number, boolean, or null.

Validation

CheckboxGroup always adds an array rule. Configured options() also adds Rule::in(...) to <field>.*, so every selected value must exist in the normalized option list. required() makes an empty array fail Laravel validation. It does not mark every native checkbox as required, which would incorrectly require every option; group errors are rendered on the field wrapper and exposed through aria-invalid.

Options with Descriptions and Icons

Each option may include a description and resolver-backed icon name for richer display:

Example
Review steps
php
CheckboxGroup::make('review_steps')->options([
    [
        'value' => 'copy',
        'label' => 'Copy review',
        'description' => 'Checks wording, tone, and grammar.',
        'icon' => 'FileCheckIcon',
    ],
    [
        'value' => 'accessibility',
        'label' => 'Accessibility review',
        'description' => 'Checks labels, contrast, and keyboard paths.',
        'icon' => 'EyeIcon',
    ],
    [
        'value' => 'legal',
        'label' => 'Legal review',
        'description' => 'Required before external publication.',
        'icon' => 'ScaleIcon',
        'disabled' => true,
    ],
])->cards()->columns(2)->default(['copy', 'accessibility']);

For options from arrays, Collections, models, or queries, choose the secondary line with optionDescription():

php
CheckboxGroup::make('feature_ids')
    ->options(Feature::query()->orderBy('name'), 'name', 'id')
    ->optionDescription('summary');

Use mapDescriptionAs() when the text needs custom PHP logic:

php
CheckboxGroup::make('feature_ids')
    ->options(Feature::query()->orderBy('name'), 'name', 'id')
    ->mapDescriptionAs(fn (Feature $feature) => "{$feature->credits} credits per month");

For simple translations, the shared option mappers also accept arrays:

php
CheckboxGroup::make('features')
    ->options(Feature::class, 'slug', 'id', 'availability')
    ->mapAs([
        'reports' => 'Reports',
        'analytics' => 'Analytics',
    ])
    ->mapDescriptionAs([
        'enabled' => 'Available now',
        'beta' => 'Beta access',
    ]);

You may also disable individual options:

php
CheckboxGroup::make('plans')->options([
    ['value' => 'free', 'label' => 'Free', 'disabled' => true],
    ['value' => 'pro', 'label' => 'Pro'],
    ['value' => 'enterprise', 'label' => 'Enterprise'],
]);

Option icons are resolver-backed icon names. See Icons.

CheckboxGroup intentionally supports a narrower option metadata contract than Combobox: description, icon, and disabled. Use Combobox::make(...)->multiple() when options need images, avatars, disabled reasons, selected suffixes, server-side search, or custom Vue option slots.

Option Sources and Mapping

options() accepts an array, Collection, Arrayable object, Eloquent model class, Eloquent or query builder, or a closure that returns one of those sources. Finite model and query sources are resolved on the server before the field is serialized.

Associative scalar arrays use each array key as the value and each item as the label. Object and array records use value and label by default. Pass label, value, and description keys as the second through fourth options() arguments, or configure them separately:

php
CheckboxGroup::make('feature_ids')
    ->options(Feature::query()->orderBy('name'))
    ->optionLabel('name')
    ->optionValue('id')
    ->optionDescription('summary');

Each key selector also accepts a closure. Use mapAs(), mapValueAs(), and mapDescriptionAs() when a resolved label, value, or description needs a lookup map or custom closure. Mapping arrays keep the original resolved value when no map entry exists.

Variants

The checkbox group supports four visual variants:

VariantDescription
defaultStandard checkboxes
cardsBordered card boxes
pillsPill/tag style
buttonsButton toolbar style

You may compare the variants with the same options:

Example
Default
Cards
Pills
Buttons
php
CheckboxGroup::make('channels')->options($channels)->variant('default');
CheckboxGroup::make('channels')->options($channels)->variant('cards');
CheckboxGroup::make('channels')->options($channels)->variant('pills');
CheckboxGroup::make('channels')->options($channels)->variant('buttons');

Shortcut methods:

php
CheckboxGroup::make('channels')->options($channels)->cards();
CheckboxGroup::make('channels')->options($channels)->pills();
CheckboxGroup::make('channels')->options($channels)->buttons();

The buttons() variant still submits an array, validates each selected value, and supports showSelectAll() when bulk selection makes sense.

Any other serialized variant falls back to default rendering in Vue and React. This keeps older or custom field data usable without creating an undocumented fifth visual style.

Columns

Display the options in a multi-column grid layout. The value is the maximum column target, and the grid automatically drops to fewer columns when the container is too narrow:

php
CheckboxGroup::make('permissions')
    ->options($permissions)
    ->columns(2);

CheckboxGroup::make('categories')
    ->options($categories)
    ->columns(3);

Inline

Display the options horizontally in a row:

php
CheckboxGroup::make('sizes')->options([
    'sm' => 'Small',
    'md' => 'Medium',
    'lg' => 'Large',
])->inline();

Select All / Deselect All

Show controls to select or deselect all options at once:

Example
Delivery regions
php
CheckboxGroup::make('regions')
    ->label('Delivery regions')
    ->options([
        'north' => 'North region',
        'central' => 'Central region',
        'remote' => 'Remote team',
    ])
    ->showSelectAll()
    ->selectAllLabel('Select all regions')
    ->deselectAllLabel('Clear regions')
    ->default(['north', 'central']);

Customize the button labels:

php
CheckboxGroup::make('permissions')
    ->options($permissions)
    ->showSelectAll()
    ->selectAllLabel('Check all')
    ->deselectAllLabel('Uncheck all');

The default labels come from the checkbox_group_select_all and checkbox_group_deselect_all translation keys. Select all includes only enabled options. Deselect all clears the current array, including any selected value whose option is disabled. The actions are hidden when the whole field is disabled.

Using the Component Directly

CheckboxGroup is exported from each stack's components subpath. Vue uses v-model; React uses controlled value and onValueChange.

vue
<script setup lang="ts">
import { ref } from 'vue'
import { Check, FileCheck, Scale } from '@lucide/vue'
import { CheckboxGroup } from '@inertiaui/form-vue/components'
import type { CheckboxGroupOption, IconResolverFn } from '@inertiaui/form-vue'

const permissions = ref(['read'])
const error = ref<string | null>(null)
const saving = ref(false)

const options: CheckboxGroupOption[] = [
    {
        value: 'read',
        label: 'Read',
        description: 'View existing entries.',
        icon: 'FileCheckIcon',
    },
    {
        value: 'write',
        label: 'Write',
        description: 'Create and update entries.',
        icon: 'CheckIcon',
    },
    {
        value: 'approve',
        label: 'Approve',
        description: 'Approve changes before publishing.',
        icon: 'ScaleIcon',
        disabled: true,
    },
]

const fieldParts = {
    option: 'rounded-lg',
    selectedOption: 'bg-blue-50',
}

const icons = { CheckIcon: Check, FileCheckIcon: FileCheck, ScaleIcon: Scale }
const iconResolver: IconResolverFn = (icon) => icons[icon as keyof typeof icons] ?? null

const trackFocus = () => {}
</script>

<template>
    <CheckboxGroup
        v-model="permissions"
        id="article-permissions"
        name="permissions"
        label="Permissions"
        help="Choose what this role may do."
        :error="error"
        :invalid="!!error"
        required
        :precognitive="false"
        :disabled="saving"
        variant="cards"
        :columns="2"
        :inline="false"
        show-select-all
        select-all-label="Select all permissions"
        deselect-all-label="Clear permissions"
        badge="Role"
        badge-class="bg-zinc-100 text-zinc-700"
        label-trailing="Required"
        label-trailing-class="text-zinc-500"
        tooltip="Disabled permissions are shown but cannot be selected."
        help-position="below"
        layout="stacked"
        control-position="start"
        label-class="font-medium"
        help-class="text-zinc-500"
        error-class="text-red-600"
        wrapper-class="max-w-xl"
        control-class="gap-3"
        class="rounded-lg"
        :part-classes="fieldParts"
        :label-sr-only="false"
        :options="options"
        :icon-resolver="iconResolver"
        data-testid="permissions"
        @focusin="trackFocus"
    />
</template>
tsx
import { useState } from 'react'
import { CheckIcon, FileCheckIcon, ScaleIcon } from 'lucide-react'
import { CheckboxGroup } from '@inertiaui/form-react/components'
import type { CheckboxGroupOption, IconResolverFn } from '@inertiaui/form-react'

const options: CheckboxGroupOption[] = [
    {
        value: 'read',
        label: 'Read',
        description: 'View existing entries.',
        icon: 'FileCheckIcon',
    },
    {
        value: 'write',
        label: 'Write',
        description: 'Create and update entries.',
        icon: 'CheckIcon',
    },
    {
        value: 'approve',
        label: 'Approve',
        description: 'Approve changes before publishing.',
        icon: 'ScaleIcon',
        disabled: true,
    },
]

const fieldParts = {
    option: 'rounded-lg',
    selectedOption: 'bg-blue-50',
}

const icons = { CheckIcon, FileCheckIcon, ScaleIcon }
const iconResolver: IconResolverFn = (icon) => icons[icon as keyof typeof icons] ?? null

export default function Permissions() {
    const [permissions, setPermissions] = useState<CheckboxGroupOption['value'][]>(['read'])
    const [error] = useState<string | null>(null)
    const [saving] = useState(false)
    const trackFocus = () => {}

    return (
        <CheckboxGroup
            value={permissions}
            onValueChange={setPermissions}
            id="article-permissions"
            name="permissions"
            label="Permissions"
            help="Choose what this role may do."
            error={error}
            invalid={Boolean(error)}
            required
            precognitive={false}
            disabled={saving}
            variant="cards"
            columns={2}
            inline={false}
            showSelectAll
            selectAllLabel="Select all permissions"
            deselectAllLabel="Clear permissions"
            badge="Role"
            badgeClass="bg-zinc-100 text-zinc-700"
            labelTrailing="Required"
            labelTrailingClass="text-zinc-500"
            tooltip="Disabled permissions are shown but cannot be selected."
            helpPosition="below"
            layout="stacked"
            controlPosition="start"
            labelClass="font-medium"
            helpClass="text-zinc-500"
            errorClass="text-red-600"
            wrapperClass="max-w-xl"
            controlClass="gap-3"
            class="rounded-lg"
            partClasses={fieldParts}
            labelSrOnly={false}
            options={options}
            iconResolver={iconResolver}
            data-testid="permissions"
            onFocus={trackFocus}
        />
    )
}

Native attributes and listeners target the rendered group element. See Native Attributes And Events.

All shared field methods remain available. See Form Class, Model Binding, Validation, Conditional Visibility, Authorization, Icons, and Styling.