Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Nova Fontawesome Laravel Package

marshmallow/nova-fontawesome

marshmallow/nova-fontawesome adds a searchable Font Awesome icon picker field to Laravel Nova. Let editors choose icons visually, store the selected class on your model, and render icons consistently across your admin resources. Lightweight and easy to install.

View on GitHub
Deep Wiki
Context7

Getting Started

This package adds one thing to your Nova toolbelt: a searchable Font Awesome icon picker that writes a class string (fa-solid fa-house) to a plain model column. Everything else — how the glyph renders, whether Pro icons are available, whether the admin bundle ships Font Awesome at all — is your application's responsibility. Treat it as a form control, not an icon system.

1. Install and check what you actually got.

composer require marshmallow/nova-fontawesome
composer show marshmallow/nova-fontawesome

Nova 5 only. v3.1.0 added Laravel 13 support — that's a constraint bump, not a behaviour change, and Nova's own Laravel support is the binding constraint in almost every upgrade conversation. Don't assume this field alone unblocks a framework bump.

2. Read the package before trusting a snippet. Open src/ and composer.json. You want three facts:

  • The exact field class and namespace (imports in blog posts drift; verify against the installed source).
  • Whether compiled Vue assets ship pre-built or are published from the service provider (look for publishes() and any Nova::script() call, and note the exact publish tag).
  • Whether the package bundles a Font Awesome CSS/icon manifest, or expects the host app to provide Font Awesome.

3. Add the column. One nullable string per icon attribute:

Schema::table('categories', function (Blueprint $table) {
    $table->string('icon')->nullable()->after('name');
});

Add it to $fillable if you mass-assign; Nova will otherwise set the attribute directly.

4. Register the field on a low-risk resource.

use Marshmallow\NovaFontawesome\NovaFontawesome;

public function fields(Request $request)
{
    return [
        ID::make()->sortable(),
        Text::make('Name'),
        NovaFontawesome::make('Icon')
            ->help('Shown in the customer-facing category nav.'),
    ];
}

First use case: a category badge, nav marker, or status label — something visible enough that you'd notice a broken picker, contained enough that a bad release doesn't block a publish action. Create or edit a record, pick an icon, save, confirm the string landed in the database, then confirm the glyph appears in your frontend template. That loop is your acceptance test for the whole package.

Implementation Patterns

Centralize the field on day two, not day thirty. The moment two resources share an icon column, drift begins — different rules, different help text, one with hideFromIndex() and one without. Put it behind a trait or base resource method:

trait HasIconField
{
    protected function iconField(string $attribute = 'icon'): NovaFontawesome
    {
        return NovaFontawesome::make($attribute)
            ->rules('nullable', 'string', 'regex:/^fa-[a-z0-9]+( fa-[a-z0-9-]+)+$/')
            ->help('Font Awesome 6/7 icon class.');
    }
}

That is now the single place to change validation, visibility policy, or swap in a fallback text input later.

Decide raw class vs. semantic key at adoption. Storing fa-solid fa-house is the zero-friction path — it's exactly what the picker emits and exactly what your Blade needs. The cost is coupling: an FA 6→7 rename becomes a data migration, and per-tenant theming means rewriting rows. If icons might ever be white-labelled or swapped by non-engineers, store a key and map it:

// config/icons.php
return [
    'category-house'   => 'fa-solid fa-house',
    'category-archive' => 'fa-solid fa-box-archive',
];
@php($icon = config('icons.' . $category->icon, 'fa-solid fa-circle-question'))
<i class="{{ $icon }}" aria-hidden="true"></i>

You lose the picker's direct output and need a translation step at every render site. Only pay that if a non-FA future or per-tenant overrides are genuinely plausible. What you must not do is leave the decision unmade and discover six months of mixed formats in production.

Whitelist at every render site. The field guarantees a string was chosen once. It does not guarantee that string is valid today — FA renames happen, rows get hand-edited, imports are wrong. Never interpolate raw:

@php
    $raw  = $category->icon;
    $safe = is_string($raw) && preg_match('/^fa-[a-z0-9]+( fa-[a-z0-9-]+)+$/', $raw)
        ? $raw
        : 'fa-solid fa-folder';
@endphp
<i class="{{ $safe }}" aria-hidden="true"></i>

{!! $category->icon !!} is an injection vector the moment anyone with Nova access can type into the field — and a nullable column edited in an admin panel is user input.

Make the index view readable. A raw class string in a table cell is noise. Render the glyph with asHtml() and an escaped class:

Text::make('Icon', fn ($category) => $category->icon
        ? '<i class="'.e($category->icon).'"></i>'
        : '—')
    ->asHtml(),

This turns the index into a quick visual audit — you can spot format drift and missing icons at a glance instead of opening records.

Seed and backfill. Existing rows are NULL, so every list view is blank on rollout day. Give factories a default and backfill the table before you remove any legacy icon column:

Category::whereNull('icon')->update(['icon' => 'fa-solid fa-folder']);

Smoke-test inside repeaters and panels before you commit. Single-purpose Nova fields most often break when mounted somewhere the component didn't anticipate — Repeater, Panel, nested Flexible content. It's a five-minute staging check that saves you discovering it after the resource is live.

Keep a text-input fallback behind a flag. If a future release breaks the picker, editors are stuck. An ->onlyOnDetail() plain Text::make('Icon') variant, or a config-gated swap, costs almost nothing and means an abandoned dependency can't block an admin release.

Gotchas and Tips

  • The package does not ship Font Awesome to your frontend. It stores classes; your app renders them. Confirm how the Nova admin gets FA — published assets, CDN via Nova::style(), or your own compiled CSS. A picker full of invisible boxes reads as broken even when it's functioning perfectly.
  • CSP and SRI. Loading FA from a CDN inside the Nova layout requires your admin CSP to allow it, and every FA version bump invalidates SRI hashes. Self-hosting FA CSS in the Nova build is the lower-maintenance option at nearly no cost.
  • Pro icons are almost certainly absent. A bundled manifest is typically free-tier only. If design specs Pro glyphs, editors will hand-type classes and the picker's value evaporates. Check coverage against your design system before promising anything.
  • Empty state is not reliably null. Depending on the component path, Nova may persist ''. Use blank() / filled() in accessors and conditionals rather than === null.
  • Format drift is the slow-burn failure. fa-solid fa-house in one resource, fas fa-house or bare house in another. The regex rule plus a centralized field definition is the whole defence. Write the convention into your adoption note so the next developer doesn't invent a third format.
  • Read the publish tag, don't guess it. A wrong vendor:publish --tag= silently does nothing and you'll spend an hour wondering why assets aren't there. After any Nova or package upgrade, run php artisan nova:publish and clear cached config/views — stale published Vue assets are the classic "picker renders blank" cause.
  • Debug a blank picker in this order: (1) browser console for a failed JS chunk — published vs. not is the dominant failure mode; (2) does the field appear in Nova's field list at all — namespace/import typo; (3) does the stored value match what the component expects. Asset problems outnumber data problems heavily.
  • Pin the version deliberately. Small package, one primary maintainer, low stars. Commit to ^3.1, note the resolved version in your lockfile notes, and upgrade on a schedule rather than absorbing an untested minor bump in a field nobody watches.
  • Extension points are thin — plan to fork, not to configure. Realistically you have ->rules(), ->help(), ->onlyOnForms()/->hideFromIndex(), subclassing the field, and overriding the Vue component if you need custom behaviour. There is no plugin surface. That's fine for a one-field package, but accept up front that a genuine feature request means vendor code, not config.
  • Keep it off revenue-critical paths. Admin-only, non-critical screens. If an icon ever gates a customer-facing publish action, make sure the text-input fallback exists so an abandoned dependency can't block an admin release.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect