Checkbox Group
Basic Usage
The CheckboxGroup field renders a group of checkboxes for selecting multiple values from a finite option list.
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:
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():
CheckboxGroup::make('feature_ids')
->options(Feature::query()->orderBy('name'), 'name', 'id')
->optionDescription('summary');Use mapDescriptionAs() when the text needs custom PHP logic:
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:
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:
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:
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:
| Variant | Description |
|---|---|
default | Standard checkboxes |
cards | Bordered card boxes |
pills | Pill/tag style |
buttons | Button toolbar style |
You may compare the variants with the same options:
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:
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:
CheckboxGroup::make('permissions')
->options($permissions)
->columns(2);
CheckboxGroup::make('categories')
->options($categories)
->columns(3);Inline
Display the options horizontally in a row:
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:
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:
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.
<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>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.