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

Filament Lucide Icons Laravel Package

codewithdennis/filament-lucide-icons

A Filament plugin that brings the Lucide icon set into your admin panels, letting you browse and select icons for forms, navigation items, buttons, and other UI elements through a convenient picker.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and register (about 10 minutes for one panel).

composer require codewithdennis/filament-lucide-icons

Register the plugin in the panel provider that should get Lucide:

use CodeWithDennis\FilamentLucideIcons\FilamentLucideIconsPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugin(FilamentLucideIconsPlugin::make());
}

Confirm the exact plugin class name and namespace against the README before you commit — it's a single-provider package, so the blast radius of a wrong guess is one error message, not a broken panel.

Where to look first, in this order:

  1. The package's composer.json. It tells you which Filament major the 4.x line targets and how Lucide is resolved (pinned vs. floating). Write the answer into the adoption ticket — nobody should have to re-derive it.
  2. The README's naming convention. Every Lucide integration keys off a prefix on the kebab-case Lucide name, e.g. lucide-alarm-clock, lucide-arrow-up-right. Get this wrong and you get a blank box, not an exception.
  3. One ->icon() call in a Resource. Prove the pipeline end-to-end before touching anything else.

First use case — the smallest thing that proves it works. Pick one Resource and set its navigation icon:

public static function getNavigationIcon(): string
{
    return 'lucide-users';
}

Run php artisan optimize:clear if you've been caching configs, load the panel, and look at the sidebar. Then convert one header Action::make('export')->icon('lucide-download') on the same Resource. If both render, the registration, the prefix, and the asset resolution are all correct — every other surface (form field prefix icons, table column icons, empty states) uses the same mechanism and will work too. If the sidebar renders a blank square, jump straight to Gotchas; the fix is almost always caching or the prefix.

Do this before you convert anything: capture a screenshot baseline of navigation, resource actions, and empty states. Icon regressions are visual, silent, and invisible to your test suite.

Implementation Patterns

Treat the icon name as a typed value, not a string. The single highest-leverage pattern for day-to-day use is an enum (or a constants class) so lucide- strings exist in exactly one place:

enum Icon: string
{
    case Users = 'lucide-users';
    case Invoice = 'lucide-receipt';
    case Approve = 'lucide-circle-check';
    case Export = 'lucide-download';
    case Empty = 'lucide-inbox';
}

Then ->icon(Icon::Approve->value) everywhere. This is what keeps the "blank glyph after a Lucide rename" class of bug down to a one-line fix instead of a grep across forty files, and it makes design review a matter of reviewing the enum rather than the codebase.

Where icons actually pay off, ranked by visibility per unit of effort:

Surface Call site Notes
Navigation getNavigationIcon(), navigationGroup() Highest visibility; do these first
Actions ->icon() on header/row/bulk actions Verbs benefit most (lucide-download, lucide-trash-2)
Form fields ->prefixIcon(), ->suffixIcon() Best on search, money, date, email inputs
Table columns IconColumn::make(), ->icon() Great for status/state; pairs with ->color()
Empty states ->emptyStateIcon() Cheap win, very visible on new panels

Override Filament's own defaults so the panel speaks one vocabulary. Registering the plugin makes Lucide available; it does not change Filament's built-in Heroicons. That's FilamentIcon::register() in a service provider:

FilamentIcon::register([
    'panels::sidebar.collapse-button' => 'lucide-panel-left-close',
    'tables::columns.icon-column.true' => 'lucide-check',
    'actions::delete-action' => 'lucide-trash-2',
]);

Scope this deliberately. "Install the plugin" and "standardise the icon language" are two separate line items — the first is a maintenance-window task, the second is an audit plus a design review. Don't let them merge into one unbounded ticket.

Blade views — use Filament's icon component, not raw @svg. Filament's component inherits color and sizing from the surrounding UI, which raw SVG injection does not:

<x-filament::icon icon="lucide-circle-alert" class="h-5 w-5 text-warning-500" />

Dynamic icons from state. The pattern that reads best in tables and infographics is mapping model state to a glyph. Keep the mapping in a method on the enum or a small helper, not inline in the column definition, so the same mapping is reusable in the Resource, the widget, and the Blade view.

Convert incrementally, by visibility. Because the plugin registers a set rather than rewriting references, there is nothing to stage — your heroicon-o-* calls keep working, right alongside Lucide, until you change them. Sequence the conversion navigation → resource labels → actions → form/table internals. Drip-feed it across existing UI polish tickets rather than making it a migration project.

Test the contracts, not the pixels. Assertions on getNavigationIcon() and action icon strings catch the "someone deleted the prefix" regression cheaply:

it('uses lucide for navigation', function () {
    expect(UserResource::getNavigationIcon())->toStartWith('lucide-');
});

Gotchas and Tips

Blank glyph, no error. This is the defining failure mode of the whole package. A wrong name, a missing prefix, a Lucide rename, or a stale asset cache all produce the same silent empty box. Debug in this order: (1) does svg('lucide-your-icon') render in a throwaway Blade view? (2) is the name exactly right — kebab-case, correct spelling, correct Lucide version? (3) did you clear caches and re-run asset publishing? Never guess; work the list.

Run the asset/cache commands after install and after every upgrade. Depending on whether icons are published as static assets or resolved via blade-icons caching, you may need php artisan filament:assets, php artisan icons:cache, and php artisan optimize:clear. Bake these into your deploy script rather than remembering them — a missing icons:cache in production while it works locally is the classic version of this bug.

Lucide renames are the real upgrade risk, and CI cannot see them. v4.1.2 bumped the Lucide dependency to v3.163.0 and added icons — a patch release that can silently degrade a glyph. Pin ^4.1.2, let 4.1.x patches flow, and gate 4.2/5.0 behind manual review. On every bump, diff your Icon enum against the new icon list and eyeball the screenshot baseline. Also verify the release date on Packagist before citing 4.1.2 in any plan — the recorded date looks anomalous.

The plugin does not rewrite your Heroicons. Every ->icon('heroicon-o-...') in resources, actions, widgets, and Blade views will keep rendering Heroicons until you change it. Audit before you start, or you'll ship a panel with two icon vocabularies and blame the plugin. grep -rn "heroicon-" app/ resources/views/ is the whole audit.

Color comes from currentColor. Lucide icons inherit text color, so ->color('danger') on an action or a text-danger-600 class on a Blade icon works as expected. If an icon looks wrong-colored, you've set an explicit fill/stroke somewhere upstream — don't fight it with !important, find the parent.

Sizing conflicts. Passing both size and a h-/w- class to the same icon component is a coin flip on which wins. Pick one convention for the codebase (classes, usually) and stick to it.

Compatibility gate is the Filament major, not this package. The realistic break scenario is upgrading Filament independently and discovering the constraint mid-upgrade. Keep a note in your upgrade runbook: check the plugin's supported Filament range before bumping Filament. The package must never be the reason a framework upgrade slips — it's a UI nicety with a one-line removal path.

Bus factor is one, and it sits in the render path of every admin screen. Mitigation is trivial because the package is tiny: it's MIT, so fork or vendor it if the maintainer goes quiet. Make that a documented contingency, not an unexamined assumption.

Extension points worth knowing. FilamentIcon::register() for defaults (see above); the plugin's config file, if published, for prefix/asset settings; and standard Laravel Blade component overriding if you need to wrap icon rendering with your own logic. Prefer these over editing anything in vendor/.

Pilot on the lowest-traffic panel first. The plugin is panel-scoped by design, so you can run Lucide on an internal tool while customer-facing panels stay on Heroicons. Ship the pilot, live with it for a week, then roll.

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.
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox