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 System Uicons Laravel Package

codeat3/blade-system-uicons

codeat3/blade-system-uicons adds Laravel Blade components for the System UIcons SVG icon set, letting you render lightweight, customizable icons directly in Blade views with simple tags—ideal for dashboards, menus, and UI details.

View on GitHub
Deep Wiki
Context7

Getting Started

Install (30 seconds). One command, no config file, no publishing required:

composer require codeat3/blade-system-uicons

The service provider is auto-discovered. There is no config/system-uicons.php to publish — the package simply registers the upstream System UIcons SVGs with blade-ui-kit/blade-icons under the prefix system-uicons, so components are available the moment Composer finishes.

Where to look first.

  1. The upstream System UIcons catalogue (https://systemuicons.com) — this is the authoritative list of icon names. The Blade component name is the upstream icon name, lowercased and kebab-cased: arrow-down, not arrow_down or ArrowDown.
  2. Inside the vendor package, vendor/codeat3/blade-system-uicons/resources/svg/ (or equivalent) — the actual files shipped, which is what your app can render. ls this directory whenever you suspect a name is missing.
  3. The blade-ui-kit/blade-icons docs — because 90% of the API you'll actually use (@svg, class merging, blade-icons:cache) comes from that parent package, not this wrapper.

First use case — drop one into a layout. The minimal smoke test:

{{-- resources/views/layouts/app.blade.php --}}
<a href="/" class="inline-flex items-center gap-2 text-gray-700 hover:text-gray-900">
    <x-system-uicons-home class="w-5 h-5" />
    <span>Home</span>
</a>

Refresh the page. If you see a house, you're done — everything else is styling and naming. If you see nothing, open DevTools and confirm an <svg> is in the DOM (see Gotchas: icons are stroke-based and inherit currentColor).

Sizing baseline. System UIcons are drawn on a 24×24 grid with a stroke weight of ~1.5 and no hard-coded width/height. Without sizing they will fill their container, so the first habit to form is always pairing the component with a size utility: class="w-5 h-5" (Tailwind) or width="20" height="20".


Implementation Patterns

1. Wrap the raw component in a project-level icon component

The single highest-leverage pattern. Don't sprinkle <x-system-uicons-* /> with ad-hoc classes across 200 views — create one thin wrapper so sizing, colour, and accessibility are decided once:

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

@php
    $sizes = ['sm' => 'w-4 h-4', 'md' => 'w-5 h-5', 'lg' => 'w-6 h-6'];
@endphp

<x-dynamic-component
    component="system-uicons-{{ $name }}"
    {{ $attributes->merge(['class' => $sizes[$size] . ' shrink-0', 'aria-hidden' => 'true']) }}
/>

Used as <x-icon name="home" /> or <x-icon name="trash" size="sm" class="text-red-600" />. This gives you a single place to swap icon sets later, a single place to add aria-hidden, and a single guard against the "no size specified" bug. It also makes name greppable and testable against the upstream catalogue.

2. Icon-only buttons — the accessibility pattern you'll use constantly

The package supplies glyphs, not semantics. For any icon-only control:

<button type="button" class="p-2 rounded hover:bg-gray-100 focus-visible:ring-2"
        aria-label="Delete record">
    <x-icon name="trash" class="text-red-600" aria-hidden="true" />
</button>

Rule of thumb: the SVG is always aria-hidden="true"; the label lives on the interactive parent via aria-label or visible text. Never rely on the icon's viewBox or filename to convey meaning.

3. Navigation / menu arrays — data-driven icons

Because it's just a Blade component name, you can drive icons from config or a PHP array. Very common in sidebars:

@php
    $nav = [
        ['label' => 'Dashboard', 'route' => 'dashboard', 'icon' => 'home'],
        ['label' => 'Customers', 'route' => 'customers.index', 'icon' => 'user'],
        ['label' => 'Settings', 'route' => 'settings', 'icon' => 'settings'],
    ];
@endphp

<nav class="space-y-1">
    @foreach ($nav as $item)
        <a href="{{ route($item['route']) }}"
           @class(['flex items-center gap-3 px-3 py-2 rounded',
                   'bg-indigo-50 text-indigo-700' => request()->routeIs($item['route'])])
           @if(request()->routeIs($item['route'])) aria-current="page" @endif>
            <x-icon :name="$item['icon']" />
            <span>{{ $item['label'] }}</span>
        </a>
    @endforeach
</nav>

This is where the package pays for itself: adding a nav entry is one array row, not a copy-paste of SVG markup.

4. Use @svg when you need the raw icon without component overhead

blade-ui-kit/blade-icons registers the @svg directive against the same prefix, so you can bypass the anonymous component:

{{-- inline, with classes --}}
@svg('system-uicons-bell', 'w-4 h-4 text-gray-500')

{{-- pass explicit attributes --}}
@svg('system-uicons-check', ['class' => 'w-4 h-4', 'aria-hidden' => 'true'])

Use this inside @if blocks or in places where using a component tag feels heavy. The <x-system-uicons-* /> form is generally more readable in markup and more IDE-friendly — prefer it in templates, use @svg for programmatic rendering.

5. Status indicators and semantic colour mapping

Because System UIcons are stroke-based and inherit currentColor, colour mapping is pure Tailwind text-*:

@php
    $statusIcons = [
        'success' => ['icon' => 'check',   'class' => 'text-green-600'],
        'warning' => ['icon' => 'warning', 'class' => 'text-amber-600'],
        'error'   => ['icon' => 'close',   'class' => 'text-red-600'],
    ];
@endphp

<span class="inline-flex items-center gap-1 {{ $statusIcons[$status]['class'] }}">
    <x-icon :name="$statusIcons[$status]['icon']" size="sm" />
    {{ $message }}
</span>

Note: apply colour with text-*, not fill-* or stroke-* — see Gotchas.

6. Strangler-style migration from inline SVG

You almost certainly have hand-pasted <svg> somewhere. Don't do a big-bang migration. Instead:

  • Add a line to the PR checklist: "new icons use <x-system-uicons-* />, not inline SVG."
  • Convert files as you already touch them.
  • A one-off regex (<svg[^>]*class="[^"]*icon-home[^"]*"...<\/svg>) is usually safe only for the simplest cases — SVG paths vary per source, so prefer manual conversion per icon name.

The system-uicons-* prefix means this package coexists with heroicons-* / fa-* during the transition without naming collisions.

7. Livewire and Inertia shells

Since this is server-rendered, it works inside Livewire components with zero changes — the icon re-renders as part of the component's view on each request:

{{-- resources/views/livewire/notification-bell.blade.php --}}
<button wire:click="toggle" aria-label="Notifications">
    <x-icon name="bell" />
    @if($count > 0)
        <span class="absolute -top-1 -right-1 text-xs">{{ $count }}</span>
    @endif
</button>

For Inertia/React/Vue surfaces, this package is the wrong seam — use the upstream System UIcons directly in JS. Scope it to the Blade shell.

8. Testing icons

If you want a render test (recommended given naming churn risk), assert on the rendered SVG class attribute — that survives icon path changes:

public function test_home_icon_renders(): void
{
    $this->blade('<x-system-uicons-home class="w-5 h-5" />')
         ->assertSee('w-5 h-5', false)
         ->assertSee('<svg', false);
}

For a broader safety net, add one test per icon actually referenced in critical layouts. A cheap way: iterate a hard-coded list of names (from your x-icon usage) and assert each renders.


Gotchas and Tips

Naming: dashes not underscores; derived from upstream, and it will churn

Component names come straight from the upstream System UIcons filenames. arrow-down, arrow-left, home, settings — hyphenated, all lowercase. If you guess wrong:

  • You get a render-time exception, not a compile error and not a warning. In blade-ui-kit/blade-icons this typically surfaces as BladeUI\Icons\Exceptions\SvgNotFound or a "component not found" error, thrown at request time. That means a bad icon name is a runtime production failure, not a build failure — which is exactly why a render test in CI is worth the ten minutes.
  • Mitigation: after any version bump, ls vendor/codeat3/blade-system-uicons/resources/svg/ and diff against the names your app actually uses. Better: keep the list of names in one place (the nav array, the <x-icon> registry) so it's greppable.
  • Upstream can rename or remove icons. If that happens you'll get render failures, not Composer errors — pin the version in composer.json (see below) and treat the icon-name list as a stable API.

Stroke, not fill — colour with text-*

System UIcons are stroke icons: the paths use stroke="currentColor" and fill="none" (or similar). Consequences:

  • class="text-red-600" works — it sets color, which currentColor picks up.
  • class="fill-red-600" does nothing useful (fills nothing, since fill="none").
  • Setting stroke="red" as an attribute works but bypasses your theme. Prefer text-*.

If you find yourself wanting filled icons, this is the wrong set — System UIcons has one consistent style.

Sizing and the "giant icon" surprise

The upstream SVGs have a viewBox="0 0 24 24" but no intrinsic width/height. Rendered without sizing they scale to their container — a <div> will make them huge. Always pair with w-5 h-5 (or explicit width/height). Setting only one dimension will distort unless you also set preserveAspectRatio="none" (don't do that — set both).

view:cache and config:cache — the classic deployment failure

This is the number-one operational gotcha for icon packages in this family, and it will bite you on your first real deploy:

  • Icons are discovered from the filesystem at render time unless cached. Some setups (including blade-icons:cache for the parent package) precompile icons into a cache file.
  • Ordering matters. In your deploy script:
composer install --no-dev --optimize-autoloader
php artisan view:clear      # clear stale compiled Blade views
php artisan config:cache
php artisan view:cache
# if you've run blade-icons:cache at any point, re-run it here too
  • If you change the icon set or upgrade the package, you must clear/re-warm the caches. A stale blade-icons cache after a version bump can render old/missing icons until you clear — and it will look like a package bug rather than a cache issue.
  • Sanity test in staging: php artisan config:cache && php artisan view:cache then load one page with icons. This is a five-minute smoke test that prevents a "icons disappeared in production" incident later.

Version pinning and the Laravel upgrade coupling

The 1.9.0 release is a single-purpose compatibility change (Laravel 13, plus the transitive blade-ui-kit/blade-icons floor — verify in composer.lock). This tells you two things:

  1. The package's only real job is to stay inside Laravel's and blade-icons' constraints. When the wrapper lags a Laravel major, your whole app upgrade blocks on it — not just icons.
  2. Pin it and be deliberate:
"require": {
    "codeat3/blade-system-uicons": "^1.9"
}

For a Laravel major upgrade, check this package's release timeline first. If it's lagging, the contingency is cheap: the payload is inert SVG, so you can fork or vendor the resources/svg directory and register it with blade-icons yourself — a half-day, not a rewrite. Note that in your upgrade runbook.

HTML weight on icon-dense pages

Every <x-system-uicons-* /> inlines a full <svg> in the HTML. That's fine for a navbar; it's a measurable cost on a table with 50 rows × 3 icons. Options, in order of preference:

  1. Enforce compression (gzip/brotli) at the edge — usually the whole fix.
  2. Reduce density: use text where an icon adds nothing.
  3. If a page is genuinely icon-heavy, render icons once into a hidden sprite <x-icon> per unique name and <use> references — but this is rarely worth the complexity; do it only if you have measured a problem.

Don't pre-optimize. Do monitor page size once you've rolled it out to a dense screen.

Attribute forwarding quirks

<x-system-uicons-* /> merges provided attributes onto the <svg> root. That means class, style, width, height, id, and aria-* all land where you expect. But:

  • Do not expect slot content to render — there is no slot; it's a self-closing SVG component. <x-system-uicons-home>Foo</x-system-uicons-home> will not place Foo inside the SVG.
  • Multiple class attributes aren't a thing; pass one merged class string. The Blade attribute bag merges duplicate class values only when you use $attributes->merge() (as in the <x-icon> wrapper above). If you're passing class from a parent, use Blade's {{ $attributes->merge([...]) }} pattern — bare interpolation of duplicate class attributes will be overwritten by the last one.

aria-hidden is not automatic

Unlike some icon wrappers, this package does not add aria-hidden="true" or a role by default (verify on install, but assume it doesn't). You'll be adding it every time unless you centralize it in the <x-icon> wrapper. This is one of the strongest practical reasons to build the wrapper from §1 of Implementation Patterns on day one.

Debugging checklist when an icon won't render

Work down this list, in order:

  1. Is the name correct? ls vendor/codeat3/blade-system-uicons/resources/svg/ | grep <name> — confirm the filename matches what you typed, in kebab-case.
  2. Is the component syntax right? x-system-uicons- prefix, hyphen-joined, self-closing.
  3. Is it visible at all? Inspect the DOM — is <svg> present? If yes but invisible, it's the width/height or currentColor issue. If no, it's the name or the cache.
  4. Caches. php artisan view:clear && php artisan config:clear (and blade-icons:clear if you used blade-icons:cache). Re-check.
  5. Composer autoload. composer dump-autoload — occasionally needed after adding the package mid-session, especially with classmap/optimized autoloaders.
  6. Config collision. If you have another icon package that also uses the system-uicons prefix (unlikely but possible), check the blade-ui-kit/blade-icons config. The wrapper registers the set in its own provider; re-registering with a custom prefix is a configuration change in your own service provider, not a package edit.

Extension points — where to look when you need more control

  • blade-ui-kit/blade-icons config (config/blade-icons.php, publishable from the parent package): set a different prefix, disable the default path, or re-register a set. This is the correct place to customize rather than modifying the vendor package.
  • Custom icon registration. If you need brand icons alongside System UIcons, register another set in blade-ui-kit/blade-icons pointing at your own resources/svg/brand directory. The two coexist cleanly.
  • @svg directive. From the parent package — useful when you need imperative rendering or to pass attributes as an array.
  • blade-icons:cache. If you use it, treat the cache file as a build artifact and regenerate on every deploy after composer install.

One last habit

Treat icon markup as a reviewable surface. When you see raw <svg …> pasted into a Blade file in a PR, that's the cue to convert it to <x-system-uicons-* />. Over a quarter this alone eliminates the majority of the design debt this package is meant to prevent.

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