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

codeat3/blade-typicons

Blade Typicons brings the Typicons icon set to Laravel as simple, self-closing Blade components. Render crisp SVG icons anywhere in your views with a clean tag syntax, no extra setup required.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and settle the prefix question first — it's the only real unknown.

composer require codeat3/blade-typicons

No provider registration, no publish step, no config file needed. Auto-discovery handles wiring, and the first icon renders from a cold install.

Pin immediately, in the same commit:

"codeat3/blade-typicons": "^1.8.0"

Zero stars, zero dependents, one maintainer. ^1.8.0 on Laravel 13; if you're on 10–12 and haven't verified the install, ^1.7.0 is the safer floor.

The prefix is genuinely ambiguous in the docs. The README shows <x-typicons::icon-name />; the wrapper convention these packages usually follow is a flat <x-typicon-name />. Drop three candidates into a throwaway route and use whichever renders — five seconds of work beats twenty minutes of reading Blade Icons source:

<x-typicon-home />        {{-- most likely --}}
<x-typicons-home />       {{-- plural variant --}}
<x-typicons::home />      {{-- namespaced variant --}}

Where to look, in priority order:

  1. vendor/codeat3/blade-typicons/resources/svg/ — the only authoritative icon-name list. ls it before you design a single screen. Every other source drifts.
  2. vendor/codeat3/blade-typicons/composer.json — the illuminate/support and blade-ui-kit/blade-icons constraints for the version you actually have.
  3. vendor/blade-ui-kit/blade-icons/README.md — the real documentation for attribute merging, set overrides, and sprite generation. This package contributes SVGs to that system; it doesn't document it.

First real use case: a nav item. It exercises install, name resolution, attribute forwarding, and sizing in one block, and failures are visible rather than buried in a table cell:

<a href="{{ route('dashboard') }}"
   class="flex items-center gap-2 px-3 py-2 rounded {{ request()->routeIs('dashboard') ? 'bg-indigo-50 text-indigo-700' : 'text-gray-700 hover:bg-gray-50' }}">
    <x-typicon-home class="w-5 h-5 shrink-0" />
    <span class="text-sm font-medium">Dashboard</span>
</a>

If that renders, you're done with setup. Everything after is architecture.

Immediately after it renders, wrap it. Not next sprint — now, while there are three icons in the codebase. See Implementation Patterns.

Implementation Patterns

1. The wrapper is the whole strategy. This is the highest-leverage decision with any single-vendor icon package, and with a 0-star one it's closer to mandatory. One internal component makes the set swappable without touching a single view.

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

@php
    $sizes = [
        'xs' => 'w-3 h-3',
        'sm' => 'w-4 h-4',
        'md' => 'w-5 h-5',
        'lg' => 'w-6 h-6',
        'xl' => 'w-8 h-8',
    ];
    $icon = config("icons.map.{$name}", $name);
    $icon = in_array($icon, config('icons.allowed', []), true) ? $icon : config('icons.fallback', 'warning');
@endphp

<x-dynamic-component
    :component="'typicon-' . $icon"
    {{ $attributes->class([$sizes[$size] ?? $sizes['md'], 'shrink-0']) }}
    aria-hidden="true"
    focusable="false"
/>

x-dynamic-component resolves at render time rather than compile time, which keeps the wrapper to a single file. Every view then writes <x-icon name="save" size="sm" /> and never mentions the vendor.

2. One semantic map, one file. Icon names describe pictures (arrow-right, tick, cog); your views need intent (next, save, settings). The moment a designer renames something or you swap sets, a semantic map turns an audit into a one-line edit:

// config/icons.php
return [
    'map' => [
        'save'     => 'tick',
        'delete'   => 'trash',
        'settings' => 'cog',
        'warning'  => 'warning',
        'empty'    => 'folder',
        'next'     => 'arrow-right',
        'back'     => 'arrow-left',
    ],
    'fallback' => 'warning',
    'allowed'  => ['tick', 'trash', 'cog', 'warning', 'folder', 'arrow-right', 'arrow-left'],
];

It doubles as your inventory of actually-used icons — the exact list you'd work from if you ever extracted the SVGs and dropped the dependency.

3. Let CSS own size and color; the component owns nothing. The root <svg> inherits currentColor, so a class on a parent control recolors the icon for free:

<button class="btn {{ $isActive ? 'text-white' : 'text-gray-600 hover:text-indigo-600' }}">
    <x-icon name="settings" size="sm" />
    Settings
</button>

Resist scatter-shot inline width/height attributes — they beat your utility classes and produce dark-mode, print, and responsive surprises that are miserable to trace back.

4. Table loops are where the package pays for itself. Uniform repeated cells, no markup duplication:

@foreach ($users as $user)
    <tr>
        <td class="px-4 py-2">
            <x-icon :name="$user->is_active ? 'tick' : 'times'"
                    size="sm"
                    class="{{ $user->is_active ? 'text-green-600' : 'text-red-400' }}" />
            <span class="ml-1">{{ $user->name }}</span>
        </td>
        <td class="px-4 py-2 text-right">
            <a href="{{ route('users.edit', $user) }}" aria-label="Edit {{ $user->name }}">
                <x-icon name="edit" size="sm" class="text-gray-500 hover:text-indigo-600" />
            </a>
        </td>
    </tr>
@endforeach

Names stay literal or config-derived. Never a DB column, never request input.

5. Accessible names go on the control, not the SVG. The component's output is decorative; aria-hidden="true" in the wrapper handles that once for the whole app. When an icon is the only content of a button or link, the label belongs on the interactive element:

<button type="button" aria-label="Delete record" class="p-2 rounded hover:bg-red-50">
    <x-icon name="trash" size="sm" class="text-red-600" />
</button>

Don't try to inject a <title> into the SVG. Attribute forwarding makes what lands in the output inconsistent across versions, and a <title> on a decorative SVG produces noisy screen-reader output layered on top of your aria-label.

6. Same code in Livewire, mailables, and PDFs — with two adjustments. Server-rendered Blade, no JS runtime. That's the real workflow advantage over a JS icon library.

  • Livewire: nothing changes; icons re-render with the component. Run php artisan view:cache in production regardless.
  • Mail / PDF: the consuming environment strips <style> and can't see Tailwind, so the utility classes vanish. Inline the styling on the SVG instead:
<x-icon name="tick" style="width:16px;height:16px;color:#16a34a;" />

7. One smoke test, pointed at the map. Cheap, and it's the only thing between a patch release and a silently blank icon somewhere in production:

public function test_all_mapped_icons_render(): void
{
    foreach (config('icons.map') as $semantic => $icon) {
        $this->blade("<x-icon name=\"{$semantic}\" />")->assertSee('<svg', false);
    }
}

Because it iterates the map, it grows automatically as you adopt icons — no test maintenance.

8. Keep the exit plausible at all times. Wrapper + config map + never using raw <x-typicon-*> tags in views means leaving is: copy the ~15 SVGs you use into resources/svg, register them via Blade Icons or a trivial own component, change one line in icon.blade.php, drop the dependency. An afternoon, not a migration.

Gotchas and Tips

Silent failure is the default failure mode — internalize this first. A typo'd or removed icon name renders nothing: no exception, no log line, no console warning. A layout gap is your only signal. When a page "looks wrong," view-source and search for <svg where the icon should be. Absent? The name is wrong. Then go straight to vendor/codeat3/blade-typicons/resources/svg/ and read the actual filename. This single scenario burns hours because people hunt for an error that was never thrown.

Never invent a name from appearance. Typicons are hyphenated (arrow-right, th-large, arrow-sync) and collisions with intuition are common — is it th-large or th-list? cog or settings? Copy from the vendor directory every time.

Namespace collisions if you already run other Blade Icons sets. Heroicons and friends register through the same blade-icons config. Wrong glyphs or unresolved components mean a set conflict:

php artisan vendor:publish --tag=blade-icons
// config/blade-icons.php
'sets' => [
    'typicon' => [
        'path'   => 'vendor/codeat3/blade-typicons/resources/svg',
        'prefix' => 'typicon',
    ],
],

Keep your prefix distinct from any existing default set so the swap path stays open.

Compiled views will lie to you. After a package upgrade, an edit to <x-icon>, or a change to the config map, run php artisan view:clear. In production, "icons vanished after deploy" is nearly always stale compiled Blade views plus a warm opcache. Pair view:clear with view:cache.

Licensing: MIT package, non-MIT artwork. The wrapper is MIT; the Typicons artwork carries its own license (CC BY-SA family), which typically obliges attribution. The package's MIT badge reads as "no strings attached" and that reading is wrong for the icons. Confirm with whoever owns brand/compliance before broad rollout and record the obligation somewhere durable — a LICENSES.md, an about screen. If you can't satisfy attribution, don't adopt.

Inline SVG volume is a rendering-strategy problem, not a package bug. 200 rows × 3 icons = 600 inline <svg> nodes in the document. Fine at normal volume; noticeable on dense tables and kanban boards. Escalation ladder, cheapest first: view:cache in production → drop icons from per-row repeated cases → move hot icons into an <x-sprite> rendered once per page and referenced with <use href="#icon-trash">. Note <use> requires the sprite in the same document, so it breaks in email clients — keep it out of mailables.

Override a single icon without forking. The SVGs are plain files consumed by Blade Icons, so copying one into your own resources/svg under the same set name wins. It's simultaneously the customization path and the abandonment insurance.

Don't feed user input into the component name. <x-dynamic-component :component="$request->input('icon')" /> resolves arbitrary Blade components, including ones you never meant to expose. Go through the config whitelist with a known-good fallback (as in the wrapper above) rather than passing raw strings.

Read the changelog before every bump. 1.8.0 is a narrow Laravel 13 compatibility PR — no features, no API change. Confirm it didn't raise the PHP floor or narrow illuminate/* past what you run:

composer why-not codeat3/blade-typicons 1.8.0

Audit coverage before committing, not in sprint three. Typicons is a compact utility set (~336 glyphs) — no brand marks, no illustrations, no multi-tone. Walk your actual screens (nav, empty states, notifications, table toolbars, date pickers) against resources/svg in an afternoon. Discovering mid-feature that there's no acceptable "upload cloud" is a far more expensive conversation than an upfront gap check. Plan for hand-rolled SVGs alongside this package; don't expect full coverage.

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.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource