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 Icons Laravel Package

driesvints/blade-icons

Blade Icons makes it easy to use any SVG icon set in your Laravel Blade views via components and directives like or @svg('camera'). Originally Blade SVG by Adam Wathan, it's the base for many third-party icon packages and supports custom icon sets.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and publish config

composer require blade-ui-kit/blade-icons
php artisan vendor:publish --tag=blade-icons

The published config/blade-icons.php ships with the default set commented out. Uncomment it and make sure the path exists:

'sets' => [
    'default' => [
        'path'   => resource_path('svg'),
        'prefix' => 'icon',
        'class'  => 'w-6 h-6', // optional default classes
    ],
],

2. Drop SVGs in resources/svg

A file at resources/svg/camera.svg is immediately available under two syntaxes:

{{-- Component syntax: <x-{prefix}-{file name} /> --}}
<x-icon-camera class="w-6 h-6 text-gray-500" />

{{-- Directive syntax: @svg($name, $class, $attributes) --}}
@svg('camera', 'w-6 h-6', ['fill' => 'none'])

3. Where to look first

  • config/blade-icons.php — sets, paths, prefixes, default classes. This is the whole mental model of the package.
  • The icon set package list — for anything standard (Heroicons, Lucide, Font Awesome, Phosphor, Tabler…), install a maintained set package instead of vendoring SVGs by hand.
  • blade-ui-kit.com/blade-icons#search — to find which set package contains a given glyph before you build a custom one.

First use case: replace hardcoded inline <svg> blobs in your layout/nav with <x-icon-* /> components so icon styling lives in Blade attributes (Tailwind classes) rather than duplicated markup. That single swap usually removes hundreds of lines of copy-pasted SVG and immediately makes icon swaps trivial.


Implementation Patterns

Map one set per source, with a meaningful prefix

Prefixes are the public API of your icons. Keep them short and stable, and let the prefix encode the source/weight so @svg() calls are self-documenting:

'sets' => [
    'default' => ['path' => resource_path('svg'), 'prefix' => 'icon'],
    'heroicon-o' => ['path' => resource_path('svg/heroicons/outline'), 'prefix' => 'heroicon-o'],
    'brand' => ['path' => resource_path('svg/brand'), 'prefix' => 'brand', 'class' => 'h-5 w-5'],
],
<x-heroicon-o-bell class="h-5 w-5" />
<x-brand-logo class="h-8" />

Subdirectories inside a set path are flattened into hyphenated names: resources/svg/actions/trash.svg → icon-actions-trash. Use that deliberately (folders as namespacing) rather than as an accident.

Wrap icons once, then reuse everywhere

Raw <x-icon-* /> calls scattered through views are hard to change globally. Add one thin wrapper for the common sizes/states and use it in feature views:

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

<x-dynamic-component :component="'icon-'.$name" {{ $attributes->class($size) }} />
<x-ui.icon name="camera" class="text-gray-500" />

Pair this with a PHP enum or constants class for icon names so typos fail loudly in tests rather than silently rendering nothing in production.

Dynamic icons

The directive accepts expressions, which is what you want for data-driven UIs (menu items, plan badges, status indicators):

@foreach ($menu as $item)
    <a href="{{ $item['url'] }}">
        @svg($item['icon'], 'w-5 h-5 mr-2')
        {{ $item['label'] }}
    </a>
@endforeach

Registering icons at runtime

When the icon source isn't a filesystem directory — a tenant-uploaded logo, an icon chosen from a DB column, a computed path — use the facade instead of relying on config:

use BladeUi\Icons\Factory;

// single closure-backed icon
BladeIcons::registerIcon('tenant-logo', fn () => $tenant->logo_svg);

// or an entire set from a non-standard location
BladeIcons::registerIcons('uploads', fn () => ...);

This is the main extension point and the one to reach for when a set doesn't fit the "directory of files" model. If you need PHP-side rendering (emails, PDFs, JSON APIs), the facade's svg() method returns an HTML string rather than relying on the Blade directive.

Caching for production

Every uncached icon lookup walks the filesystem. Cache the manifest in your deploy pipeline:

php artisan icons:cache

...and remember the inverse — php artisan icons:clear — is what you run whenever the on-disk icon set changes without a fresh build. A typical deploy order is:

php artisan icons:clear
composer install --no-dev --optimize-autoloader
php artisan icons:cache
php artisan config:cache && php artisan view:cache

Styling and accessibility

  • Icons are inlined as raw SVG, so currentColor is the integration seam with Tailwind. Ship icons with fill="currentColor" / stroke="currentColor" and control colour purely via classes on the component.
  • Decorative icons: <x-icon-camera aria-hidden="true" />. Meaningful icons: add role="img" plus <title> in the SVG file, or render visually-hidden text next to it.
  • In Livewire/Alpine views the component syntax behaves like any other Blade component — attributes like wire:click, x-on:click, @click pass straight through to the root <svg>.

Testing

Add one smoke test that iterates your enum/constants list and asserts each icon resolves, so a renamed or deleted file fails CI instead of 500-ing on a page:

foreach (IconName::cases() as $icon) {
    $this->blade("<x-icon-{$icon->value} />")->assertSee('<svg', false);
}

Gotchas and Tips

The default set is commented out after publishing. The single most common "package is broken" report: @svg('camera') throws SvgNotFound because the set was never uncommented or the path doesn't exist. Validate the path first.

SvgNotFound is an exception, not a blank render. A missing icon in a Blade view becomes a 500 in production. Guard dynamic icon names (@svg($icon ?? 'placeholder'), or a file_exists-style helper at the boundary where the name enters the app) rather than trusting DB/user input.

Stale manifest after deploy is the #1 production mystery. With icons:cache in place, newly deployed SVG files are invisible until the cache is cleared. Make icons:clear idempotent and always run it before icons:cache in the deploy script.

Config changes need a config cache refresh. Editing blade-icons.php in an environment with config:cache has no effect until php artisan config:cache is re-run. Same class of bug, different cache.

Prefix collisions resolve by registration order and will bite silently. Two sets claiming the same prefix, or a set with an empty prefix (only usable via @svg()), produce confusing "wrong icon renders" behaviour. Keep prefixes unique and always non-empty if you want the <x-... /> sugar.

Names are derived from filenames. Camera.svg vs camera.svg, spaces, and odd characters all matter; kebab-case everything and avoid renaming shipped icons — the rename is a breaking change for every template that references them.

Set-level class is a default, not a merge guarantee. Per-set class and attributes act as baseline attributes on the root <svg>; anything you pass on the component/directive is layered on top. If an icon file hardcodes class="w-6 h-6" internally, expect a fight with your Tailwind overrides — strip presentation attributes from the source SVGs and let the calling template own them.

Everything is inlined, so there is no tree-shaking. icons:cache speeds up resolution, not output size. Rendering a 200-row table of icons ships 200 copies of the SVG markup. For icon-heavy UIs, prefer a sprite/<use> approach or a smaller set of glyphs, and keep an eye on response size.

Never render untrusted SVG through this package. SVGs can carry <script> and event handlers. Tenant-uploaded or admin-pasted icons must be sanitised (e.g. with an SVG sanitiser library) before being written to disk or returned from registerIcon, otherwise you have stored XSS with extra steps.

Debugging tip: when an icon "should" work, check in this order — config cache, icons:cache manifest, the actual filename/directory mapping, then the prefix. php artisan icons:clear plus a local @svg() call with a fully-prefixed name isolates the problem to config vs. file layout almost immediately.

Extension points worth knowing: BladeIcons::registerIcon() / registerIcons() (dynamic sources), per-set class/attributes in config (global default styling), the @svg directive (programmatic rendering), and the icons:cache/icons:clear commands (performance and deploy hooks). Almost everything else in day-to-day use is just naming conventions.

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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
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
cgv/localized-resource