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

codeat3/blade-ikonate

Use Ikonate's customizable SVG icons directly in Laravel Blade views. This package provides simple Blade components for embedding icons with minimal markup, keeping templates clean and icon usage consistent across your app.

View on GitHub
Deep Wiki
Context7

Getting Started

One command, no build step. The package is a thin wrapper around blade-ui-kit/blade-icons that ships the Ikonate SVG set as Blade components. Laravel auto-discovery registers the provider; nothing goes in config/app.php and there are no assets to publish.

composer require codeat3/blade-ikonate

Look at the SVG directory before you look at the README. The vendored svg/ folder is the only authoritative list of component names — the README's icon list drifts from whatever upstream Ikonate snapshot got bundled:

ls vendor/codeat3/blade-ikonate/svg | sed 's/\.svg$//' | column -c 100

Then open one raw file before you build any theming assumption on top of it:

head -c 400 vendor/codeat3/blade-ikonate/svg/search.svg

You want to confirm three things: a viewBox (not hardcoded width/height), stroke="currentColor" (so CSS color inheritance works), and whether stroke-width sits on the root <svg> or on individual paths (this determines whether you can restyle weight — see Gotchas).

First use case: an icon in a nav link or button. Drop it in and inspect the rendered HTML immediately:

<a href="{{ route('dashboard') }}" class="flex items-center gap-2 text-slate-600 hover:text-slate-900">
    <x-ikonate-home class="w-5 h-5 shrink-0" aria-hidden="true" />
    <span>Dashboard</span>
</a>

Three call styles exist, all backed by blade-icons:

{{-- 1. Component tag (preferred in templates) --}}
<x-ikonate-search class="w-4 h-4 text-slate-400" />

{{-- 2. Directive --}}
@svg('ikonate-search', 'w-4 h-4 text-slate-400')

{{-- 3. Helper — returns Htmlable, useful from PHP --}}
{!! svg('ikonate-search', 'w-4 h-4') !!}

Decide the prefix on day one. Publishing the config is optional, but if you ever want <x-search /> instead of <x-ikonate-search />, do it now — changing the prefix later is a repo-wide find-and-replace.

php artisan vendor:publish --tag=blade-ikonate-config

Read the published file before editing: the key names differ slightly between blade-icons v1 and v2, and the prefix key is what drives your call sites.

Wire the cache commands into your local loop immediately, even if you only plan to cache in production. Discovering stale-cache behaviour during a deploy is worse than learning it on a laptop:

php artisan icons:clear && php artisan view:clear

Implementation Patterns

Wrap the package in your own component before it spreads. This is the highest-leverage hour you'll spend. Call sites should name your icon API, never the vendor package — then swapping the underlying set is a one-file change instead of a template sweep.

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

@php
    $classes = 'w-5 h-5 shrink-0 ' . ($attributes->get('class') ?? '');
    $attrs = $attributes->except('class')->merge(
        $label
            ? ['role' => 'img', 'aria-label' => $label]
            : ['aria-hidden' => 'true', 'focusable' => 'false']
    );
@endphp

{!! svg('ikonate-' . $name, $classes, $attrs->getAttributes()) !!}

Call sites become uniform:

<x-ui.icon name="search" class="w-4 h-4 text-slate-400" />
<x-ui.icon name="trash" label="Delete record" class="text-red-600" />

<x-dynamic-component :component="'ikonate-'.$name" /> is a tidier alternative if your pinned version resolves it reliably — verify before standardizing. The svg() helper path works across versions and is the safe default.

Centralize size and color as tokens, not per-call classes. Ikonate is stroke-based and inherits currentColor, so state color is free from the parent:

<button type="button"
        class="group inline-flex items-center gap-2 rounded-md px-3 py-2
               text-slate-700 hover:bg-slate-100 hover:text-indigo-600">
    <x-ui.icon name="download" class="w-4 h-4 text-slate-500 group-hover:text-indigo-600" />
    <span>Export</span>
</button>

Because attributes land on the <svg> root, Tailwind utilities apply directly — no [&>svg] selector gymnastics. Pick a three-step scale (w-4 h-4 inline, w-5 h-5 nav, w-6 h-6 feature) and enforce it in review; sizing drift is what makes an icon set look mismatched.

Drive icon-bearing menus from config, not hand-written markup. Nav arrays with an icon key plus your wrapper keep icon wiring out of templates:

// config/ui.php
return ['nav' => [
    ['route' => 'dashboard',     'label' => 'Dashboard', 'icon' => 'home'],
    ['route' => 'reports.index', 'label' => 'Reports',   'icon' => 'bar-chart'],
    ['route' => 'settings.edit', 'label' => 'Settings',  'icon' => 'settings'],
]];
@foreach (config('ui.nav') as $item)
    <a href="{{ route($item['route']) }}"
       @class(['flex items-center gap-2 rounded px-3 py-2',
               'bg-slate-100 text-indigo-600' => request()->routeIs($item['route'])])>
        <x-ui.icon :name="$item['icon']" />
        <span>{{ $item['label'] }}</span>
    </a>
@endforeach

Guard icon-name typos in CI. A bad name throws BladeIcons\Exceptions\SvgNotFound at render time, which means a typo on a rarely-hit error page can ship. A cheap test renders every icon name found in your views:

it('renders every icon referenced in blade views', function (string $name) {
    expect(Blade::render("<x-ikonate-{$name} />"))->toContain('<svg');
})->with(fn () => collect(File::allFiles(resource_path('views')))
    ->flatMap(fn ($f) => preg_match_all('/x-ikonate-([a-z0-9\-]+)/', $f->getContents(), $m) ? $m[1] : [])
    ->unique()->values()->all());

Adjust the regex to your wrapper convention (name="..." vs. tag suffix). This doubles as your early-warning system if an upgrade renames a glyph upstream.

Handle accessibility once, in the wrapper. Decorative icons get aria-hidden="true"; meaningful icons get role="img" + aria-label. Doing it in one place means no developer has to remember. If you need <title> semantics, put aria-label on a wrapping <span> — these components render a bare <svg> and aren't content-bearing slots.

Watch icon density per page. Inline SVG is ideal on a settings page and expensive in a 500-row table. Two levers that don't change call sites:

php artisan icons:cache   # production deploy step — avoids re-parsing the set per request

And for genuinely dense grids, render the set once into an SVG sprite (<symbol> + <use href="#ikonate-search">) via a small partial, then have your wrapper emit the <use> form on table pages. That's an extension you build — the package doesn't ship it. Budget it only if HTML weight is measurable.

Keep a fork contingency written down. Because you're already on blade-ui-kit/blade-icons, you can copy the svg/ directory into resources/svg/ikonate, register that path as a set with the same ikonate prefix, and drop the Composer dependency — with zero template edits. Name an owner for this before you need it.

Upgrade routine. Pin "codeat3/blade-ikonate": "~1.7.0". The 1.7.0 release is a Laravel-13 compatibility bump with no API changes and no new icons, so the routine is: bump the constraint, composer update codeat3/blade-ikonate, php artisan icons:clear, smoke-test one page.


Gotchas and Tips

Tailwind Preflight sets svg { display: block; }. An icon dropped into a sentence or a button label jumps to its own line. Fix with inline-block align-text-bottom at the call site, or — the more common pattern — put flex items-center on the parent. This bites every new developer exactly once.

Verify stroke-width control before promising it to design. If Ikonate sets stroke-width on the root <svg>, an attribute on the component cascades. If it's on individual <path>s, it won't, and you need a utility instead:

<x-ikonate-check class="w-5 h-5 [&_path]:stroke-[1.5]" />

Don't assume. Check the raw file, then test in isolation.

No solid or duotone variants — and don't fake them. Ikonate is a focused, stroke-based 24px set. Setting fill="currentColor" on a stroke-only icon produces a blobby mess. If design asks for a filled "active" state, use a color or weight change as the indicator, or source a second set. Accept this constraint at the design-review stage, not mid-sprint.

SvgNotFound is the exception you'll actually debug. The message names the set and prefix, which is usually enough. Fast triage:

php artisan icons:clear
ls vendor/codeat3/blade-ikonate/svg | grep -i yourname
php artisan tinker --execute="echo svg('ikonate-home');"

Renders in tinker but not in the browser? It's caches — icons:cache from a previous deploy, view:cache, or config:cache holding a stale published config path.

Deploy order matters when icons are cached. php artisan icons:cache must run after composer install and after any config publish, otherwise the compiled set points at a path that doesn't exist yet. Put it immediately after composer install --no-dev, next to view:cache.

Everything you pass lands on the <svg> root. Great for class, aria-*, role, width, height. Also means a stray style="..." becomes an inline style that collides with a strict style-src CSP. The package's SVGs are attribute-only (no inline <style> or <script>), so the vendor code is CSP-benign — the risk is self-inflicted.

Inline SVG inflates HTML — measure before rewriting. A ~5KB icon × 300 table rows is real payload. If a page gets noticeably heavier after adoption, check response size first; icons:cache and a sprite partial are your two levers, in that order.

Livewire works, but audit icon-heavy lists. These render server-side and diff fine, but a wire:model.live search on a 200-row table re-renders hundreds of inline SVGs per keystroke. Put wire:loading on the action, not per-row, and debounce the input.

Don't ship these in emails or PDFs. Major mail clients (Gmail notably) strip or mangle inline SVG, and DomPDF/Snappy render stroke-based SVG inconsistently. The onboarding-email use case that looks obvious in a demo is the one that silently breaks. Use PNGs for mail; if you must generate PDFs, push one icon through the actual pipeline before building a template around it.

Treat the svg/ directory as the contract, the README as a convenience. Icon names come from upstream Ikonate, not from you. A future wrapper refresh can rename or drop a glyph — your CI icon test catches that on upgrade day rather than in production.

One maintainer, near-zero adoption. The 1.7.0 release is a "still alive" signal and a genuine unblocker for a Laravel 13 upgrade, not a support commitment. Pin the version, keep the wrapper thin, keep the vendoring steps documented, and don't put this package on a path where an icon failing to render stops a customer from paying you.

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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons