Filament is a powerful admin panel for Laravel, but sometimes your UI needs go beyond its built-in fields. In this tutorial, we’ll walk through how to build a custom checkbox dropdown component in Filament that supports multiple columns and compatible with dark mode.
Goal
We want to display a dropdown that, when opened, shows a list of checkboxes (e.g. for selecting items). The checkboxes should:
- Be selectable via checkboxes
- Be arranged in multiple columns
- Store selections in a Livewire model
- Work with dark mode
The purpose for this component is, I have a list of more than 100 entries. If I take CheckboxList component, all of these entries take so much space in UI. Similarly, If I add multiselect Select component, It will not show all entries at once and user have to type name to search for entries.
So, dropdown will reduce the space in UI and checkboxes solve searching problem.
Step 1: Create the Custom Field Component
Create the filament custom checkbox dropdown field component file at app/Forms/Components/CheckboxDropdown.php
and add the following code.
<?php
namespace App\Forms\Components;
use Filament\Forms\Components\Field;
class CheckboxDropdown extends Field
{
protected string $view = 'forms.components.checkbox-dropdown';
protected array $options = [];
protected int $checkboxColumns = 1;
public function options(array $options): static
{
$this->options = $options;
return $this;
}
public function getOptions(): array
{
return $this->evaluate($this->options);
}
public function checkboxColumns(int $count): static
{
$this->checkboxColumns = $count;
return $this;
}
public function getCheckboxColumns(): int
{
return $this->evaluate($this->checkboxColumns);
}
}
Step 2: Create a blade view file for the component
As mentioned in the component class, create a custom checkbox dropdown component view file at resources/views/forms/components/checkbox-dropdown.blade.php
and add the following code.
@php
$options = collect($getOptions())->mapWithKeys(fn ($label, $id) => [(string) $id => $label]);
$jsonOptions = $options->toJson();
$gridCols = match ($getCheckboxColumns()) {
1 => 'grid-cols-1',
2 => 'grid-cols-2',
3 => 'grid-cols-3',
4 => 'grid-cols-4',
default => 'grid-cols-1',
};
@endphp
<div
x-data="{
open: false,
toggle() { this.open = !this.open },
selected: @js($getState() ?? []),
liveSelected: @entangle($attributes->wire('model')).defer,
options: {{ $jsonOptions }} || {},
isSelected(id) {
return this.selected?.includes(id);
},
labelFor(id) {
if (!this.options || typeof this.options !== 'object') return id;
return this.options[id] ?? id;
}
}"
class="relative">
<!-- Trigger Button -->
<button
type="button"
@click="toggle"
class="w-full border rounded px-3 py-2 text-left bg-white dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-gray-800 dark:text-gray-200 shadow-sm">
<template x-if="selected?.length">
<span x-text="selected.map(labelFor).join(', ')"></span>
</template>
<template x-if="!selected?.length">
<span class="text-gray-400 dark:text-gray-500">Select items...</span>
</template>
</button>
<!-- Dropdown Panel -->
<div
x-show="open"
@click.away="open = false"
x-cloak
class="absolute z-10 w-full mt-1 rounded border bg-white dark:bg-gray-900 border-gray-300 dark:border-gray-700 shadow max-h-60 overflow-y-auto">
<ul class="p-2 grid {{ $gridCols }} space-y-1 gap-2">
<template x-for="(label, id) in options" :key="id">
<li>
<label class="flex gap-2 items-start space-x-2 text-gray-800 dark:text-gray-200">
<input
type="checkbox"
class="fi-checkbox-input rounded border-none bg-white shadow-sm ring-1 transition duration-75 checked:ring-0 focus:ring-2 focus:ring-offset-0 disabled:pointer-events-none disabled:bg-gray-50 disabled:text-gray-50 disabled:checked:bg-gray-400 disabled:checked:text-gray-400 dark:bg-white/5 dark:disabled:bg-transparent dark:disabled:checked:bg-gray-600 text-primary-600 ring-gray-950/10 focus:ring-primary-600 checked:focus:ring-primary-500/50 dark:text-primary-500 dark:ring-white/20 dark:checked:bg-primary-500 dark:focus:ring-primary-500 dark:checked:focus:ring-primary-400/50 dark:disabled:ring-white/10 mt-1"
:value="id"
:checked="isSelected(id)"
@change="
if (isSelected(id)) {
selected = selected.filter(i => i !== id)
} else {
selected.push(id)
}
liveSelected = selected;
">
<span x-text="label"></span>
</label>
</li>
</template>
</ul>
</div>
</div>
Now this component id ready to use. Currently, it can adopt current filament admin panel theme and you can distribute checkboxes to multiple columns using checkboxColumns
option.
Step 3: Usage in Filament Resource
You can use this custom checkbox dropdown component in any resource file as follows:
CheckboxDropdown::make('selected_items')
->label('Select Items')
->options(Item::pluck('name', 'id')->toArray())
->checkboxColumns(3)
Conclusion
With this setup, you can develop a fully reusable, dynamic, and user-friendly multi-column checkbox dropdown — perfect for any Laravel Filament project.