datomatic/nova-icon-field
A Laravel Nova icon field that gives you a searchable picker for selecting icons from your preferred icon set and storing the chosen icon on your model. Clean, intuitive UI that fits naturally into Nova forms and resources, with configurable icon sets and easy installation.
datomatic/nova-icon-field is a Laravel Nova 4 field that renders a searchable icon picker in your resource forms and saves the chosen icon identifier as a plain string column. It is a Nova field only — there is no standalone UI, no frontend renderer, and no icon-font dependency baked in. v1.0.2 is a patch on the 1.0 line, so everything below applies to ^1.0.
Install
composer require datomatic/nova-icon-field
php artisan vendor:publish --tag=nova-icon-field-config
The package assumes Nova 4 is already installed and configured. Publish the config before writing any field code — this is the first file to open, because it is the authoritative list of icon sets the picker can offer. If a set is not in config/nova-icon-field.php, no field can use it.
Add the column
Schema::table('services', function (Blueprint $table) {
$table->string('icon')->nullable()->after('name');
});
A nullable string(255) is the right default. The value is a single identifier like account-group or fa-solid fa-user; only move to JSON if you genuinely need multiple icons per record (in which case you probably want a custom field, not this one).
First field
use Datomatic\NovaIconField\Icon;
public function fields(NovaRequest $request)
{
return [
Icon::make('Icon')
->setIconSets(['material-design-icons'])
->rules('required', 'max:255'),
];
}
First thing to check when it breaks: if the form renders but the picker grid is empty, the string passed to ->setIconSets() does not match a key in the published config, or the config entry points at a missing JSON file. That single mismatch accounts for most first-run failures.
Scope icon sets per field, not globally. The config lists everything you have installed; each field should declare only what is relevant. A "brand logo" attribute and a "status badge" attribute rarely want the same multi-thousand-icon catalogue — and every set you enable is paid for in admin bundle size.
Separate the form field from the display field. The picker is for editing; tables and detail views usually want a rendered icon or a plain name:
Icon::make('Icon')
->setIconSets(['material-design-icons'])
->onlyOnForms(),
Text::make('Icon', fn () => "<i class=\"mdi mdi-{$this->icon}\"></i>")
->asHtml()
->onlyOnIndex(),
Store the neutral identifier, resolve per surface. Save account-group, not mdi mdi-account-group. Keep a small map in your app (config/icons.php, an enum, or a IconLibrary service) that turns the identifier into the right markup for each consumer — mdi mdi-account-group in the admin, an inline SVG on the marketing site, nothing in a PDF export. This turns "switch icon library" into a config change instead of a data migration, and it is the single most valuable habit with this package.
Validate server-side. The picker constrains input in the browser only; Nova will persist whatever string arrives via a crafted request, an import, or an API path. Build an in rule from the same JSON the field loads, or write a small rule that checks membership in the enabled sets:
Icon::make('Icon')
->setIconSets(['material-design-icons'])
->rules(['required', 'string', 'max:255', Rule::in(IconSet::names('material-design-icons'))]),
Factories and seeders are trivial — but be deliberate. Do not invent icon names by hand; copy real identifiers out of the published JSON so seeded rows render on first load instead of showing an empty box in the index table.
Adopt incrementally over free-text columns. If you already have a varchar holding fa fa-star, those values will not match picker identifiers. Backfill through a lookup table, keep the picker nullable and validation lenient until the backfill completes, then tighten it.
Localise set labels in the config, not scattered across fields, if you run more than one Nova locale.
vendor:publish never overwrites. After every upgrade, diff the package's config against yours — new set keys or renamed options will silently fail to appear, and the symptom looks like a broken field rather than a stale config.
Large icon sets are heavy. The picker ships the set's metadata to the browser; Material Design Icons alone is thousands of entries. On slow admin connections the first paint of the form is noticeably worse. Trim the JSON to the subset you actually use, or keep large sets off heavy index screens.
Search is client-side only. There is no lazy loading or server-side query. When an editor says "the icon isn't there", they are searching the wrong set — confirm ->setIconSets() before debugging anything else.
Rendering is your responsibility. The package gives you the selector; how the icon appears in tables, emails, PDFs and exports is app-side. Decide early whether the column stores the bare name (portable) or the full CSS class (faster to render, couples data to one library) — and do not mix the two conventions in the same column.
Stale Nova assets after an upgrade. Nova caches compiled assets. After bumping the package, run php artisan nova:publish and hard-refresh before assuming a regression in v1.0.2 — the release notes are only a changelog link, so the compare diff on GitHub is the reliable source of what actually changed.
Pin the constraint. Use "datomatic/nova-icon-field": "^1.0" rather than tracking dev-main. The package is small and infrequently released; a floating constraint gives you no early warning when the field API shifts.
Check Nova major compatibility before upgrading Nova. The field API has diverged between Nova 4 and 5, and a patch bump on a low-traffic package does not guarantee it tracks a new Nova major. Verify against the README's matrix first.
Know the extension boundary. Multi-select icons, custom SVG uploads, or a user-generated icon catalogue are out of scope by design. Wrap the picker inside your own Nova field if you need composition, or drop to a JSON column with your own component. Bending Icon into those shapes typically ends in a fork you now maintain.
Where it stops earning its keep: if the icon catalogue is remote or user-managed (a brand asset library, per-tenant icons), a thin custom field over your own API is less work than hand-syncing a JSON file into config/nova-icon-field.php.
How can I help you explore Laravel packages today?