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

Blade Lucide Icons Laravel Package

inlst/blade-lucide-icons

A Laravel package for rendering Lucide icons in Blade templates. It provides Blade components and directives for the full Lucide icon set, with options for size, color, stroke width, and CSS classes, making icons easy to use consistently across your views.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a thin Blade/Filament wrapper around the Lucide icon set: it registers a Blade component (and, in Filament, an icon set / plugin hook) so you can render any Lucide glyph by name without importing SVGs by hand. As of v1.40.0 the bundled Lucide snapshot was synced forward through twenty upstream releases (Lucide 1.29.0 → 1.49.0) in one drop — the PHP API did not change, but the icon catalogue did.

Minimal setup:

composer require inlst/blade-lucide-icons:^1.40

# Publish the config (tag may differ — check the README)
php artisan vendor:publish --tag=blade-lucide-icons-config

If it ships a Filament plugin, register it on the panel in app/Providers/Filament/AdminPanelProvider.php:

use Inlst\BladeLucideIcons\BladeLucideIconsPlugin;

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

If it's Blade-only, the service provider is auto-discovered and the component is available immediately. Your first successful use case should be one nav icon and one inline icon:

{{-- Blade --}}
<x-lucide name="settings" class="w-5 h-5" />

{{-- Filament resource --}}
public static function getNavigationIcon(): string
{
    return 'lucide-settings'; // confirm prefix from config
}

Where to look first, in order:

  1. The package's README.md — it is the only reliable source for the component name, config keys, and Filament registration signature for this specific fork.
  2. config/blade-lucide-icons.php after publishing — the prefix, path/disk, and default class/size keys control almost everything.
  3. src/ — usually 3–6 files. Look for the Blade::component(...) or Blade::componentNamespace(...) call in the service provider; that tells you the exact tag name to use.
  4. resources/views/ — if views are publishable, this is where the SVG wrapper and currentColor/stroke-width defaults live.
  5. resources/svg/ (or wherever the config points) — post-1.40 this directory is a much larger, much newer snapshot. If an icon vanished, this is where you confirm it.

Do not start with a bulk migration of Heroicons. Render two icons, confirm they appear in a Filament panel and a plain Blade view, then scale.

Implementation Patterns

1. Standardize on your own wrapper component. Given the package is a 1-star, 0-dependent project, the highest-leverage move is a one-file indirection so a future swap (to mallardduck/blade-lucide-icons, blade-ui-kit/blade-heroicons, or a raw SVG sprite) is a single edit:

{{-- resources/views/components/icon.blade.php --}}
@props(['name', 'size' => 20])

<x-lucide
    :name="$name"
    {{ $attributes->merge(['class' => 'shrink-0']) }}
    width="{{ $size }}"
    height="{{ $size }}"
    aria-hidden="true"
/>

Then every view uses <x-icon name="user" /> and the underlying package tag never leaks into product code. This seam matters more after 1.40.0, not less — see the Gotchas on icon churn.

2. Filament nav/resource/action icons. The common day-to-day use is replacing emoji or Heroicon strings on resources, pages, and actions:

protected static ?string $navigationIcon = 'lucide-layout-dashboard';

// Actions
Action::make('export')->icon('lucide-download');

Keep a small constant map in a config or enum so icon strings aren't scattered — and so a post-upgrade rename is one edit, not a grep:

// config/ui.php
'icons' => [
    'dashboard' => 'lucide-layout-dashboard',
    'users'     => 'lucide-users',
    'billing'   => 'lucide-credit-card',
],

3. Dynamic icons from data (with a whitelist). Rendering a database-driven icon is fine if you validate against a known set. This is the pattern that most often goes wrong (see Gotchas):

$allowed = ['lucide-home', 'lucide-star', 'lucide-bell'];
$icon = in_array($menu->icon, $allowed, true) ? $menu->icon : 'lucide-circle';

4. Pin and upgrade deliberately. v1.40.0 is a pure data refresh (20 Lucide releases at once), so the risk is not in PHP code you can review — it's in the SVG snapshot. Pin "inlst/blade-lucide-icons": "1.40.*", upgrade in a branch, and diff the rendered output rather than reading the changelog, which will only ever say "update Lucide".

5. Migration from Heroicons, incrementally. Do not regex-replace heroicon-o-* — the name sets differ. Run a small mapping table (heroicon-o-home → lucide-house, heroicon-o-cog → lucide-settings) and migrate surface-by-surface, starting with the navigation, because that's your highest-visibility, lowest-risk area.

6. Testing — now with an upgrade gate. Assert the component renders rather than asserting SVG bytes:

it('renders the dashboard icon', function () {
    $this->blade('<x-icon name="lucide-home" />')
        ->assertSee('<svg', false)
        ->assertSee('lucide-home');
});

Add one smoke test per icon your app actually references (not just a sample), driven from the same config map you use in Filament. Since the wrappers typically fail soft — an unresolved name yields an empty <svg> rather than an exception — a passing test suite is the only cheap way to detect that 1.40.0 dropped or renamed a glyph you depended on. Better still, iterate the map:

it('resolves every configured icon', function () {
    foreach (config('ui.icons') as $icon) {
        $html = Blade::render('<x-icon name="'.$icon.'" />');
        expect($html)->toContain('lucide');
    }
});

7. Design-system consistency via CSS, not props. Let the SVG inherit color and size from the parent instead of passing size everywhere. A single rule like svg.lucide { width: 1em; height: 1em; stroke-width: 1.75; } keeps icons consistent across Filament tables, buttons, and Blade views.

Gotchas and Tips

  • This release is a catalogue swap, not a code change. v1.40.0 contains zero features, fixes, or deprecations beyond syncing Lucide 1.29.0 → 1.49.0. Treat it as an SVG dependency bump: the review question is "did any glyph I use get renamed or removed?", not "does the package still work?". Nothing in src/ should need touching.
  • Icon renames are the real upgrade hazard. Across twenty Lucide minors there are many renames and alias drop-offs (the home → house, alert-circle → circle-alert, edit-* → pen/square-pen, more-horizontal → ellipsis family). Whether your app breaks depends entirely on whether the wrapper resolves old names via aliases or only exact filenames. Run the config-map smoke test above against 1.40.0 before merging.
  • Brand icons are gone upstream. Lucide deprecated and then removed logo glyphs (github, twitter, facebook, chrome, …). If you were leaning on this package for social icons, 1.40.0 is the release where they stop rendering. Plan to vendor those few SVGs yourself.
  • Maturity risk remains the headline gotcha. 1 star, 0 dependents, single maintainer — you are effectively the QA team. Pin the exact minor version in composer.json and treat upgrades as reviewed changes, not composer update noise.
  • Icon naming mechanics. Lucide files are kebab-case (arrow-right); some wrappers accept PascalCase or add a lucide- prefix themselves. Passing lucide-lucide-arrow-right silently renders nothing or an empty <svg>. Check the provider's Blade::component call before assuming the prefix.
  • Empty <svg> means "not found", not "broken CSS". Most wrappers return an empty SVG when the name doesn't resolve. If an icon is invisible — especially right after this upgrade — first check that the file exists under the configured path, then look at Tailwind.
  • fill vs stroke. Lucide is stroke-based (fill="none" stroke="currentColor"). If you inherit a Tailwind class that sets fill-current (common when swapping out a Heroicon solid), you'll get filled blobs. Reset with fill="none" on your wrapper.
  • Color inheritance breaks if the SVG hardcodes a color. Confirm the published view uses currentColor, not #000. Newer Lucide snapshots occasionally shift how attributes are emitted, so re-verify the published view after a data refresh like this one.
  • Filament alias collisions. Filament has its own icon aliases (heroicon-o-*, filament-*). If both the package and a panel plugin register an icon set, last-registered wins. If <x-icon> works but ->icon('lucide-...') doesn't, the Filament-facing registration is the problem — not the Blade component.
  • Config/registry caching. After changing the config or upgrading, run php artisan config:clear and php artisan view:clear. If the package ships an icon-cache command, include it in your deploy script; if it doesn't, don't assume caching exists — and don't assume a cached manifest was invalidated by the version bump.
  • Never interpolate user input straight into the name prop. Wrappers that do file_get_contents(base_path($path . $name . '.svg')) are trivially path-traversable (../../.env). Always whitelist dynamic icons server-side.
  • Accessibility. Decorative icons need aria-hidden="true"; meaningful ones need a <title> or adjacent visible text. The package wrapper almost certainly doesn't handle this for you.
  • Debugging checklist when nothing renders: (1) is the service provider auto-discovered — check composer.json extra.laravel.providers; (2) does Blade::getClassComponentAliases() list the tag; (3) does the SVG file exist under the configured path (post-1.40, also check it wasn't renamed); (4) is the panel plugin registered after your theme's provider.
  • Extension points to lean on: the published config (prefix, default class/size — cheap to change), the publishable view (override once, keep the package's resolution logic), and your own <x-icon> wrapper (the real seam). Avoid forking the package; the wrapper gets you 90% of the flexibility with none of the maintenance.
  • Have an exit plan. Because the repo is small and single-maintainer, keep the wrapper component's API (name, size, class) close to what the popular alternatives expose. If the package is abandoned — or a future bulk icon sync breaks a glyph you can't work around — swapping the one line inside components/icon.blade.php is a 10-minute change instead of a codebase-wide grep.
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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource