codeat3/blade-fontisto-icons
Blade Fontisto Icons provides simple Blade components for the Fontisto icon set, letting you drop scalable SVG icons into any Laravel view with a single tag. Install via Composer, then render icons by name with optional size and styling—no asset pipelines or extra configuration needed.
Install (30 seconds).
composer require codeat3/blade-fontisto-icons
No service-provider registration, no php artisan vendor:publish, no config file, no asset build step. Laravel auto-discovery registers the provider and the package exposes its SVG set as Blade components. Confirm the resolved graph before you commit:
composer show codeat3/blade-fontisto-icons # version, requires
composer show blade-ui-kit/blade-icons # the transitive dependency that matters
composer why codeat3/blade-fontisto-icons
First use case — drop an icon into a view. The documented form is a single component with a name prop:
<x-fontisto-icon name="home" class="w-4 h-4 text-gray-500" />
Depending on the registered prefix in the installed version, the per-icon form <x-fontisto-home /> may also (or instead) be what's available. Verify once, at the top of your first spike, then never think about it again:
ls vendor/codeat3/blade-fontisto-icons/resources/svg | head -30 # the actual vocabulary & exact names
sed -n '1,120p' vendor/codeat3/blade-fontisto-icons/src/*ServiceProvider.php
Render one icon in a scratch route and view-source it. If you see <svg …> in the HTML, you are done — do not proceed to build anything else until you've seen inline SVG.
Where to look first, in order:
vendor/codeat3/blade-fontisto-icons/resources/svg/ — this directory is the documentation. Icon names are the filenames minus .svg. Treat it as the source of truth, not the upstream marketing site.blade-ui-kit/blade-icons.composer.json of the package — the illuminate/* and php constraints. The 1.6.0 notes say "Laravel 13 compatible" but say nothing about the floor; if you are on Laravel 11/12, this line is what tells you whether you were dropped.Do this before the first real page: create your own wrapper and make it the only thing feature views are allowed to use.
resources/views/components/icon.blade.php
The first use case that pays for the whole package is a nav or dashboard: home, search, user, settings, bell, chevron-down. Roughly ten glyphs cover most chrome, and Fontisto has all of them.
1. Wrap the vendor component on day one. The single highest-value thing you will write is a five-line adapter. It gives you accessibility defaults the vendor component doesn't inject, one place to pin sizing, and a one-file swap if you ever change icon sets.
{{-- resources/views/components/icon.blade.php --}}
@props(['name', 'title' => null])
@if ($title)
<x-fontisto-icon
:name="$name"
role="img"
aria-label="{{ $title }}"
{{ $attributes->merge(['class' => 'inline-block shrink-0']) }}
/>
@else
<x-fontisto-icon
:name="$name"
aria-hidden="true"
focusable="false"
{{ $attributes->merge(['class' => 'inline-block shrink-0']) }}
/>
@endif
Then ban <x-fontisto-icon> outside that file via a review checklist or a grep in CI:
! grep -rl 'x-fontisto-' resources/views --exclude=components/icon.blade.php
2. Sizing and colour are CSS, not props. The package renders inline SVG, so the icon inherits currentColor and is sized by the box you give it. Prefer a class over an inline width/height, and never pass numbers as attributes:
<x-icon name="search" class="size-5 text-slate-500 hover:text-slate-900" />
If an icon refuses to scale, the SVG file itself carries hardcoded width/height attributes. Beat them with CSS (svg { width: 1em; height: 1em } on your wrapper class) rather than editing vendor files.
3. Icon + label vs icon-only button. Icons inside a labelled button or link are decorative — aria-hidden is correct, and the visible text is the accessible name. Icon-only controls need the label moved onto the control, not the icon:
<button type="button" class="p-2 rounded hover:bg-slate-100">
<x-icon name="trash" class="size-4" />
<span class="sr-only">Delete invoice</span>
</button>
4. Dynamic icon names — whitelist, always. A status badge is the most common reason you'll want a variable name. Never interpolate user data:
@php
// closed set, defined in PHP — not from the request
$statusIcon = match ($order->status) {
'paid' => 'check',
'pending' => 'clock',
'failed' => 'close',
'shipped' => 'truck',
default => 'question',
};
@endphp
<x-icon :name="$statusIcon" class="size-4" />
If you must go fully dynamic, use @switch/match or an enum map to a literal, and keep the name prop server-controlled. <x-dynamic-component :component="'fontisto-icon'"/> driven by request input is an injection surface and a guaranteed InvalidArgumentException at 2am.
5. Build an internal icon gallery — this is the real enablement artifact. The vocabulary is ~600 glyphs and discovery is the actual support burden, not defects. One route, one view, no dependencies:
Route::get('/dev/icons', function () {
$files = glob(base_path('vendor/codeat3/blade-fontisto-icons/resources/svg/*.svg'));
return view('dev.icons', [
'icons' => collect($files)->map(fn ($f) => basename($f, '.svg'))->sort()->values(),
]);
})->middleware('can:viewDevTools');
<div class="grid grid-cols-6 gap-3">
@foreach ($icons as $icon)
<div class="flex flex-col items-center gap-1 p-2 border rounded">
<x-icon :name="$icon" class="size-6" />
<code class="text-[10px] break-all">{{ $icon }}</code>
</div>
@endforeach
</div>
Gate it behind a dev-only middleware or app()->isLocal(). Every "does this set have a printer icon?" question now has a URL answer.
6. Migrating off a webfont or CDN set. Keep the wrapper's prop surface identical to the old class convention so the swap is mechanical. Fontisto's own CSS classes are fi fi-*; map them once:
{{-- before --}} <i class="fi fi-home"></i>
{{-- after --}} <x-icon name="home" class="size-4" />
Sweep file by file, not with a blind regex — fi-check → check is safe, but compound names (arrow-right, angle-down, file-1) will silently become arrowright and render nothing.
7. Testing. Icons are server-rendered, so assertSee works, and you can assert on the component name in tests that care about markup:
$response->assertSee('aria-hidden="true"', false);
$this->get('/dashboard')->assertOk(); // smoke-test: a bad icon name blows up render
A cheap regression guard: a test that loops your app's icon constants and asserts each SVG file exists. It catches vendor-set changes on composer update before production does.
8. Versioning policy. Pin the minor ("^1.6"), let the lockfile govern, and treat the package as a leaf presentation dependency — never let an icon component type leak into domain code, jobs, or mailables' logic (mailables are fine; use the component, keep no icon logic there).
arrow-right, angle-down, checkbox-active, sometimes numeric suffixes (file-1). Guess-and-check wastes hours; ls the svg directory once and keep the gallery from pattern 5 deployed on dev.blade-icons' fallback configuration it can render an empty SVG — a blank space that looks like a layout bug. Add the icon-literal regression test in pattern 7 and you'll never chase one.currentColor means fill won't work as you expect. Colour comes from color, not fill. If a Tailwind text-* class appears ignored, check whether a parent is setting fill directly.<svg>, not a wrapper. Your class merges into the SVG's own class list — useful, but it means margins and flex behaviour apply to the SVG box. Wrap in a <span> only when you genuinely need a positioned container.width/height. Many icon distributions do. Override in CSS rather than editing vendor files (which composer update will silently revert).<x-icon> wrapper is where aria-hidden / role="img" / <title> belong. Audit your wrapper once, and every icon in the app inherits the fix.php artisan view:clear, then re-check. Also confirm you're in a Blade view — this package does nothing for Inertia/Vue/Livewire templates unless you're writing Blade syntax inside them.composer.json for a raised floor before treating it as a no-op.blade-ui-kit/blade-icons and its illuminate/* constraints aren't. In a scratch branch, actually run the resolution against your target framework version.fontisto- prefix (or you're using multiple codeat3/blade-*-icons packages), verify the registry. Low likelihood, but the symptom — one icon set winning over the other — is confusing to debug from a view.composer audit and a periodic composer outdated on this line are worth a calendar reminder. The escape hatch is already built if you followed pattern 1: swapping icon sets is a change to icon.blade.php plus a name-mapping table, not a template rewrite.<x-app-icon> component beside it rather than inheriting maintenance of ~600 files.How can I help you explore Laravel packages today?