wallacemartinss/filament-icon-picker
A beautiful, modern icon picker for Filament v5 powered by blade-icons. Features a modal grid with real-time search, icon set filtering, infinite scroll, and previews. Works in forms, table columns, and infolists, with support for Heroicons, FontAwesome, Phosphor, Material, and more.
Three things need to happen before the picker will even render correctly, and only one of them is composer require.
1. Install the package and at least one icon set.
composer require wallacemartinss/filament-icon-picker
php artisan filament-icon-picker:install-icons # interactive menu
# or non-interactive:
php artisan filament-icon-picker:install-icons --all
The installer is the part worth using over manual composer require calls: it also generates the bundled PHP enums (Heroicons, PhosphorIcons, GoogleMaterialDesignIcons, …) into Wallacemartinss\FilamentIconPicker\Enums.
2. Wire the vendor views into your Filament theme. This is the single most commonly missed step. In your panel theme CSS:
/* resources/css/filament/admin/theme.css */
@source '../../../../vendor/wallacemartinss/filament-icon-picker/resources/views/**/*';
Then npm run build. Skip this and the field works but the modal grid, search box and scrolling look broken/unstyled.
3. Publish config and warm the icon cache.
php artisan vendor:publish --tag="filament-icon-picker-config"
php artisan optimize:clear
php artisan icons:cache # optional, but do it if you shipped many sets
Where to look first
config/filament-icon-picker.php — the allowed_sets array is the knob you'll touch most; the config comment is explicit that you use set names (heroicons, fontawesome-solid), not package names (blade-heroicons).Wallacemartinss\FilamentIconPicker\Enums\Icon — the helper class (Icon::heroicon('users', 'outlined')) is the least-ceremony way to produce valid icon strings, and it's the one thing you can use immediately without generating anything.vendor/wallacemartinss/filament-icon-picker/resources/views — worth a skim so you understand what Tailwind is scanning and how the modal is composed.First use case
The 10-minute win is an icon column on an existing resource, e.g. a Category:
// database
$table->string('icon')->nullable();
// form
use Wallacemartinss\FilamentIconPicker\Forms\Components\IconPickerField;
IconPickerField::make('icon')
->label('Icon')
->searchable()
->allowedSets(['heroicons']),
// table
use Wallacemartinss\FilamentIconPicker\Tables\Columns\IconPickerColumn;
IconPickerColumn::make('icon')->large()->primary(),
If that renders a searchable modal on create/edit and a coloured glyph in the table, your Tailwind and icon-set setup are both correct.
The field persists the string the modal produces (heroicon-o-users, fas-heart, phosphor-whatsapp-logo-duotone). Keep the DB column a plain string and resolve to enums only at the point of use. That way adding/removing icon packages never requires a data migration, and IconPickerColumn / IconPickerEntry read the column directly with zero conversion.
Global allowed_sets in config is a blunt instrument. In practice you want a different constraint per resource — a "social links" table wants fontawesome-brands, a nav builder wants heroicons + phosphor. Use the fluent method and leave config global as a maximum:
IconPickerField::make('icon')->allowedSets(['fontawesome-brands'])
IconPickerField::make('icon')->allowedSets(['heroicons'])->showSetFilter(false)
showSetFilter(false) matters when you've narrowed to one set — the dropdown becomes dead UI.
icon()The most useful non-obvious feature: IconPickerColumn and IconPickerEntry accept icon(fn ($record) => ...), letting you use them as computed status indicators without a database column at all.
IconPickerColumn::make('health')
->icon(fn ($record) => $record->is_active ? 'heroicon-s-check-circle' : 'heroicon-o-x-circle')
->color(fn ($record) => $record->is_active ? 'success' : 'danger')
->animation(fn ($record) => $record->is_processing ? 'spin' : null)
->showLabel()
This is the pattern I'd reach for on dashboard-ish tables. Note showLabel() on the column and showIconName(false) on the entry are inverses — don't assume symmetry between the two components.
Two different jobs, two different tools:
IconPickerField + IconPickerColumn/IconPickerEntry, stored in DB.Icon helper. Never a free-text field.For nav/actions, prefer the helper over the generated enums when the choice is dynamic or the icon set might change:
protected static string|BackedEnum|null $navigationIcon = Icon::heroicon('users', 'outlined');
Action::make('delete')->icon(Heroicons::OutlinedTrash); // enum → autocompletion
Use enums where you want IDE autocomplete and typo safety; use Icon::* where the argument comes from config or user input.
If you have more than a handful of resources, centralise the defaults rather than repeating ->searchable()->modalSize('5xl') everywhere:
// app/Filament/Support/Fields.php
public static function icon(string $name = 'icon'): IconPickerField
{
return IconPickerField::make($name)
->searchable()
->placeholder('Choose an icon…')
->modalSize('4xl')
->allowedSets(config('app.icon_sets', ['heroicons', 'phosphor-icons']));
}
Validation of an icon column against the installed set is otherwise a silent-failure generator. Use the enum's search() / options() as the rule source, or validate with a Rule::in() built from Heroicons::options() in a form request if the column is also writable via API.
Heroicons::options(); // ['OutlinedStar' => 'heroicon-o-star', ...]
Heroicons::search('star'); // matching cases
For seeders, Heroicons::OutlinedStar->value reads better and survives an icon-set version bump better than a hardcoded string.
Set names ≠ package names. The config uses heroicons, fontawesome-solid, phosphor-icons, google-material-design-icons. Passing blade-heroicons yields an empty picker with no error. When in doubt, run php artisan filament-icon-picker:install-icons --list and copy the names verbatim.
Enums go stale. Enums are generated at install time. Install another icon package later and those icons exist in the picker but not in your enum — autocomplete silently lies. Re-run:
php artisan filament-icon-picker:generate-enums --all
icons:cache after every icon-set change. Blade Icons caches its manifest. Add a package, forget the cache, and the new icons don't appear. php artisan icons:cache (or optimize:clear) is the fix, and it's the first thing to try for any "icons missing" report.
Filament v5 only. Requirements are PHP 8.2+, Laravel 12+, Filament 5.0+. This will not install on a v3 or v4 panel, and with zero dependents there's no ecosystem pressure for a backport. Pin the constraint in composer.json ("^1.0" rather than *) and read CHANGELOG.md before minor bumps — the config/component API surface is still young.
Config caching. Because allowed_sets lives in config, php artisan config:cache in deploy will freeze your set list. If you vary sets per-tenant or per-panel at runtime, use ->allowedSets() in code instead of config, or you'll be purging config cache on deploy.
Invalid stored values fail silently. A row containing fa-solid-star (wrong prefix) or an icon from an uninstalled set renders as blank in the column/entry — no exception, no log. If icon columns are editable outside the picker (imports, API, legacy data), add a small validation or a null-safe fallback in the column ->icon(fn ($record) => $record->icon ?: 'heroicon-o-question-mark-circle').
Large sets + infinite scroll. Phosphor/Material are 9k–10k icons. Search is client-side-ish (real-time filter by name), so the initial modal payload matters — restrict allowedSets to what you actually need, and prefer ->modalSize('4xl')+->showSetFilter(false) for single-set fields to reduce the rendered grid.
Colour accepts Tailwind classes too. ->color('text-purple-500') works alongside the named Filament colours and raw CSS. Useful when the design system doesn't map to success/danger, but note it bypasses Filament's dark-mode colour pairing — verify in dark mode.
Testing. Livewire-test the field by asserting the underlying state rather than the DOM grid:
livewire(CreateCategory::class)
->fillForm(['icon' => 'heroicon-o-star'])
->call('create')
->assertHasNoFormErrors();
$this->assertDatabaseHas('categories', ['icon' => 'heroicon-o-star']);
Don't assert on icon HTML — the Blade Icons inline SVG output is version-sensitive.
Extension points are thin. The package exposes config, allowedSets, modalSize, showSetFilter, and the size/colour/animation methods. There is no documented way to register a custom icon source or override the modal view beyond publishing vendor views. If you need a bespoke picker behaviour, plan to fork or compose your own modal around blade-icons directly rather than fight the component.
How can I help you explore Laravel packages today?