codeat3/blade-humbleicons
A Laravel Blade component package that brings the Humbleicons icon set directly into your views. Render any icon with a simple x-humbleicon tag, adjust size and other attributes inline, and avoid copying raw SVG markup into your templates.
Install. One command, nothing else:
composer require codeat3/blade-humbleicons
There is no config file to publish, no assets to compile, no service provider to register. Laravel's package auto-discovery registers the Blade components on the next boot. The icons ship inside vendor/, so a normal composer install (including --no-dev) is all your deploy needs — but a deploy that skips composer install and ships a stale vendor/ will ship stale icons.
Where to look first. Don't guess icon names from a website. List what you actually have in the version you installed:
ls vendor/codeat3/blade-humbleicons/resources/svg | head -50
The filename is the component name, minus the .svg, kebab-cased. So arrow-left.svg becomes <x-humbleicons-arrow-left />. That directory listing is your authoritative catalogue and it is pinned to the tag in your lockfile — a name that exists on the Humbleicons website may not exist in the version you installed, and vice versa.
First use case. The realistic entry point is a single nav or action row where someone has pasted raw <svg> markup into a Blade file. Replace it:
{{-- before: 8 lines of pasted SVG --}}
<a href="{{ route('settings') }}" class="flex items-center gap-2 text-gray-700 hover:text-gray-900">
<x-humbleicons-settings class="w-5 h-5" />
<span>Settings</span>
</a>
Two things are already working there and you should verify them on day one: the icon inherits currentColor from the text colour (so hover states just work), and the class attribute wins over the SVG's intrinsic width="24" height="24" because CSS beats presentation attributes.
Naming convention to publish immediately. Decide once and write it down: always <x-humbleicons-* />, always size with classes, never size with inline width/height, decorative icons always get aria-hidden="true". This is the entire "guidelines" document for the package, and having it makes the exit a search-and-replace.
The wrapper component pattern (do this). Don't scatter <x-humbleicons-search class="w-4 h-4" /> through 200 files. Wrap it once so conventions live in one place and swapping the icon set later is a one-file change:
{{-- resources/views/components/ui/icon.blade.php --}}
@props(['name', 'size' => 'w-5 h-5'])
<x-dynamic-component
:component="'humbleicons-' . $name"
{{ $attributes->class([$size, 'shrink-0']) }}
aria-hidden="true"
/>
Usage becomes <x-ui.icon name="trash" class="text-red-600" />. Note shrink-0 — without it, flex children squash your icons into ellipses when a label wraps, which is the single most common visual bug you'll hit.
Accessibility pass-through. Standard HTML attributes land on the root <svg>, so:
{{-- decorative, label beside it --}}
<x-humbleicons-search class="w-4 h-4" aria-hidden="true" />
{{-- icon-only button --}}
<button type="button" aria-label="Close" class="p-2">
<x-humbleicons-x class="w-5 h-5" aria-hidden="true" />
</button>
Icons inside buttons are almost always decorative — the button carries the accessible name. Putting aria-label on the SVG instead of the button is a frequent miss.
Dynamic icon names. A Blade component tag cannot interpolate its own name (<x-humbleicons-{{ $name }} /> is a parse error). Use the underlying blade-icons helper, which returns an SVG object:
{!! svg('humbleicons-' . $navItem->icon)->toHtml() !!}
Treat the source as untrusted if it comes from the database — validate $navItem->icon against a whitelist array, because an unknown name throws rather than rendering nothing.
Coexistence while migrating. Component names are namespaced by the humbleicons- prefix, so Heroicons/Tabler/Font Awesome Blade packages can sit side by side. Migrate file by file as you touch it — no big-bang cutover, no feature freeze. If you need a visual A/B during the transition, that's the reason to keep the wrapper component from day one.
Status and empty states. These are the highest-value, lowest-effort uses: swap a bare <p>No results</p> for an icon + heading + copy block, or put a status icon in front of a table cell. Colour it with text-emerald-600 / text-amber-600 / text-rose-600 and the stroke follows via currentColor — no per-icon colour prop needed.
Smoke test for the icons you use. A misspelled or removed icon fails at render, not at boot, which means it fails in production on a page nobody clicked in staging. Add a test that renders exactly your inventory:
public function test_all_icons_in_use_render(): void
{
foreach (config('ui.icons') as $name) {
$this->blade("<x-ui.icon name=\"{$name}\" />")->assertSee('<svg', false);
}
}
Keep the list in config rather than crawling the codebase — it also doubles as documentation of your dependency surface.
Grouped dependency updates. Put this package in the same automated-update group as your other Laravel view packages, pinned at ^1.2. The 1.2.0 release is maintenance-only (widening the framework constraint for Laravel 13), so it's safe to take; automation should handle it, not a human calendar reminder.
Unknown icon names throw, they don't fail silently. BladeUI\Icons\Exceptions\SvgNotFound is raised at render time. That's good — you get a loud failure — but it means a bad name in a rarely visited admin page surfaces in production. The smoke test above is the only real defence. Also check the raw response when debugging locally: the exception message names the icon set and the missing name, which points you straight at the filename mismatch.
Stroke vs. fill. Humbleicons are stroke-based. Setting fill="currentColor" does nothing useful and can double-render outlines; use stroke-* and text-* colour utilities. If you set stroke-width via attribute it applies to the root <svg> and cascades to paths that don't declare their own — but if the source SVG sets it per-path, your attribute loses. Verify with devtools once rather than assuming, and set the stroke width in the wrapper component if you need it globally.
Don't use these in email templates. Inline <svg> is stripped or ignored by Outlook and a large slice of webmail clients. If you have Blade-rendered transactional email in the same repo, the icon layer stops at the browser boundary — use PNG or text there.
HTML payload on icon-dense pages. Inline SVG means every icon is repeated verbatim in the markup. A table with 50 rows × 3 action icons is a lot of duplicated bytes, and it re-renders inside every Livewire component update that touches the region. For genuinely icon-heavy surfaces, either switch those spots to a single sprite sheet with <use href="#...">, or use wire:ignore on the static container so Livewire doesn't re-emit the SVGs on each poll.
Clear compiled views after upgrading the package. Icons are resolved through the Blade compiler in some code paths; after bumping the dependency run php artisan view:clear before concluding that an icon is missing. Same for composer update on a CI box that caches storage/framework/views.
Tailwind scanning. Class names in <x-humbleicons-* class="..."> are literal in .blade.php files, so Tailwind's scanner finds them — no safelist needed. But if you build a class string dynamically ('text-' . $color . '-600') for icon colouring, those classes will be purged. Keep colour classes as whole literals or safelist them.
Global default attributes live in blade-icons, not here. The transitive blade-ui-kit/blade-icons package owns the config (config/blade-icons.php), including a global default class applied to every icon. If you need to force a baseline size across all icon packages at once, that's the lever — but check the transitive constraint, because that range, not this package, is what determines your Laravel 13 compatibility window.
Icon inventory changes on upgrade. Upstream Humbleicons is a small, single-maintainer set. Icons can be added (fine) and, rarely, renamed or removed (a render-time break). After any version bump, diff the resources/svg directory against what your code actually references — that diff is the real changelog, since this wrapper's release notes only cover packaging and framework constraints.
Treat it as a leaf dependency. Zero stars, zero dependents, one maintainer. Nothing about it touches domain logic or boot, so the safe posture is: pin it, smoke-test it, group it into automated updates, and if Composer resolution blocks a framework upgrade, drop it from the upgrade branch and reintroduce it afterwards. The exit is a search-and-replace on the <x-humbleicons- prefix — keep it that way by never letting the raw tags leak into 50 files without the wrapper component.
How can I help you explore Laravel packages today?