codeat3/blade-element-plus-icons
A Laravel Blade package that makes Element Plus icons available as simple, ready-to-use Blade components. Install via Composer and render scalable SVG icons in your views with clean, expressive tags, no manual SVG copying required.
Install with Composer — there is no npm package, no Vite plugin, no asset publish step:
composer require codeat3/blade-element-plus-icons
Laravel's package auto-discovery registers the vendored SVG set with blade-ui-kit/blade-icons, so the very first icon renders the moment the install finishes. Nothing to configure, no migrations, no service provider to add to config/app.php.
First, settle the prefix before you write a single tag. The README shows <x-ep-plus-{name} /> while earlier write-ups reference <x-element-plus-{name} />. Don't guess — confirm it in the source:
grep -rn "prefix\|register" vendor/codeat3/blade-element-plus-icons/src/*.php
The service provider's ->prefix('...') call is the ground truth. Also confirm the set actually loaded:
php artisan list | grep -i icon # shows cache/clear/check commands from blade-icons
First render. Drop one icon into any Blade view:
{{-- resources/views/dashboard.blade.php --}}
<button class="inline-flex items-center gap-2 rounded px-3 py-2">
<x-ep-plus-edit class="w-4 h-4" />
Edit
</button>
If you see the glyph, you're done with setup. If you see an Unable to locate a class or view for component error, the prefix is wrong — try the other one.
Where to look first:
vendor/codeat3/blade-element-plus-icons/resources/svg/ — the actual file names are the component names. This directory is your icon gallery and your naming reference; there is no reliable online gallery for this specific wrapper.config/blade-icons.php (published from blade-ui-kit/blade-icons) — where fallback, class, and cache settings live.Best first use case: a settings/admin page or a nav. Icons there are decorative, low-risk, easy to eyeball against the Element Plus design language, and the diff is trivial to revert if the package disappoints.
The component is a thin wrapper around an <svg> root element, so every attribute you pass lands on the root:
{{-- Tailwind sizing --}}
<x-ep-plus-search class="h-4 w-4 text-gray-400" />
{{-- Explicit dimensions --}}
<x-ep-plus-close width="16" height="16" />
{{-- Colour via currentColor — inherits from the parent --}}
<a href="#" class="text-blue-600 hover:text-blue-700">
<x-ep-plus-download class="mr-1 h-4 w-4" />
Download
</a>
Element Plus glyphs are authored with currentColor, so text-* utilities just work. Treat class as your only styling surface; avoid inline fill/stroke overrides.
The single highest-leverage pattern is your own thin wrapper, which decouples templates from the package's prefix and gives you one place to enforce sizing and accessibility:
{{-- resources/views/components/ui/icon.blade.php --}}
@props(['name', 'size' => 'md'])
@php
$sizes = ['sm' => 'h-4 w-4', 'md' => 'h-5 w-5', 'lg' => 'h-6 w-6'];
$component = 'ep-plus-' . $name;
@endphp
<x-dynamic-component
:component="$component"
{{ $attributes->merge(['class' => $sizes[$size] ?? $sizes['md']]) }}
aria-hidden="true"
focusable="false"
/>
Usage becomes <x-ui.icon name="edit" size="sm" />. If the package is ever abandoned or the prefix changes, you edit one file — not 300 views. Do this from day one; retrofitting it later is tedious.
Rather than repeating h-4 w-4 on every call site, set a baseline in config/blade-icons.php:
'class' => 'inline-block shrink-0', // applied to every icon root
Keep it to layout-neutral properties; sizing belongs to call sites or your wrapper.
blade-icons throws at render time when a component name doesn't exist — which means a typo becomes a 500 on a page you didn't test. Set a fallback so unknown names degrade to a neutral glyph:
// config/blade-icons.php
'fallback' => 'ep-plus-question-filled',
This is cheap insurance, especially when icon names are built dynamically.
For status badges, icon pickers, or data-driven navigation:
@php
$statusIcon = match ($order->status) {
'paid' => 'circle-check-filled',
'pending' => 'clock',
'failed' => 'circle-close-filled',
default => 'info-filled',
};
@endphp
<x-dynamic-component :component="'ep-plus-' . $statusIcon" class="h-4 w-4" />
Because the icon set is vendored rather than generated at build time, these resolve at runtime with no precompilation step. But note: uncached, this hits the filesystem per render (see below).
blade-icons sets register independently under their own prefixes, so you can run Heroicons and Element Plus side by side with zero conflict:
composer remove the old pack once usage hits zero.Rollback is reverting component tags — no data, schema, or build implications.
This is the one operational step that matters. The set registers ~1,000 SVG components; caching compiles them into a single manifest:
php artisan icons:cache # or blade:icon-cache — check `php artisan list`
Add it to your deploy script after config:cache and before your health check, and clear it on deploy so container restarts pick up new SVGs. On a containerised platform, an uncached cold start on a large page is visibly slow, and different pods can render inconsistently if some cached and some didn't. Treat the cache command as a deploy gate, not an optimisation.
The tempting win is reusing the same icon vocabulary in transactional email and invoices. It works, with caveats: Gmail strips inline <svg>, and Dompdf's SVG support is limited to a subset (you'll want php-svg-lib and simple paths only). Test in the actual target renderer before you build a design system on it; a <img> with a data-URI PNG is the reliable fallback for email.
Icon names are not intuitive. Element Plus has both edit and edit-pen, and they are different glyphs. ls vendor/codeat3/blade-element-plus-icons/resources/svg/ is your authoritative list — do not trust memory or another icon set's naming. Codify approved names in your front-end style guide before a second developer starts using the package, or you'll get three different "settings" icons in the same screen.
Verify the prefix early, not mid-sprint. The x-ep-plus-* vs x-element-plus-* ambiguity in the docs will cost you a confusing Unable to locate a class or view for component error. Grep the service provider once, then write it in the style guide.
These SVGs have no intrinsic width/height. Most Element Plus glyphs ship with only a viewBox (commonly 0 0 1024 1024), so an un-sized icon will expand to fill its container and look enormous. Always pass explicit sizing — either class="h-4 w-4" or width/height attributes. This trips people up on the very first render.
Accessibility is on you. Decorative icons need aria-hidden="true" and focusable="false" (the latter for IE/legacy Edge, harmless elsewhere). Meaningful icons — icon-only buttons, status indicators — need role="img" and an aria-label. The wrapper component above is the right place to enforce this; retrofitting aria attributes across a codebase is miserable.
The icon set lags upstream. These SVGs are a manual re-sync of @element-plus/icons-vue. A brand-new Element Plus icon won't exist here until the maintainer regenerates and tags a release. Don't block a feature on it — check the vendored directory first and hand-author a one-off <svg> in your wrapper if needed.
Pin to a caret and review minor bumps. "codeat3/blade-element-plus-icons": "^1.2" prevents a future major from silently riding in, but even minor bumps can resync upstream icons and cause visual drift. Diff the SVG directory between versions if a UI regression appears after a routine composer update.
Version 1.2.0 is purely a compatibility release. It widens the illuminate/* ceiling to Laravel 13 — no API changes, no deprecations. Treat it as a prerequisite for the Laravel 13 upgrade and bump it as part of that work, not after it. Sequence it last if you're mid-upgrade so constraint conflicts surface outside the framework PR; verify with composer why-not codeat3/blade-element-plus-icons 1.2.0.
Single maintainer, one GitHub star, zero dependents. This is fine for decorative glyphs and unacceptable for anything where a missing icon is a hard availability event. Mitigation: for regulated or transactional UI, wrap the icons in your own Blade component (as above) and mirror the SVG files into resources/ so the package is a one-file swap if it dies. For everything else, composer remove plus a find-and-replace on tags is a fine contingency.
Caching subtleties. The cache manifest is a build artifact — it must ship with the image. If your Dockerfile runs icons:cache in a layer that gets invalidated, or your CI caches bootstrap/cache across branches, you can get a stale manifest that doesn't match the vendor directory, producing "icon works locally, 404s in prod." Clear and rebuild it as part of every deploy.
Don't escape the output. <x-ep-plus-* /> already emits safe, trusted markup. Use the component syntax, never {!! file_get_contents(...) !!} around SVG paths — you lose the attribute passthrough and reintroduce XSS surface for no benefit.
Debugging checklist when an icon doesn't render:
php artisan view:clear — Blade's compiled views can hold a stale component reference after an install.php artisan icons:clear && php artisan icons:cache — rebuild the manifest.circle-check-filled, not check-circle-filled).config/blade-icons.php under 'sets'.x-element-plus-*).Extension point worth knowing: because blade-icons supports multiple registered sets, you can register your own directory of hand-authored SVGs alongside Element Plus under a custom prefix (e.g. brand-* for logos and product marks). That keeps one consistent <x-*icon*> vocabulary across the app instead of half components and half raw <svg> blobs.
How can I help you explore Laravel packages today?