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 Icon Picker Laravel Package

nickperkins/blade-icon-picker

A standalone icon picker Blade component for Livewire forms that works with any blade-icons pack, featuring search, lazy rendering, keyboard navigation, and responsive styling with no Tailwind dependency.

View on GitHub
Deep Wiki
Context7

Getting Started

Install (5 minutes). Two Composer packages and one publish command:

composer require nickperkins/blade-icon-picker
composer require blade-ui-kit/blade-heroicons   # or any blade-icons pack you already have
php artisan vendor:publish --tag=icon-picker-assets

Then two tags in your layout — CSS in <head>, JS at the end of <body> after Alpine:

<link rel="stylesheet" href="{{ asset('vendor/icon-picker/icon-picker.css') }}">
...
<script src="{{ asset('vendor/icon-picker/icon-picker.js') }}"></script>

Livewire 3 auto-registers the Alpine component; no Alpine.data() call is needed. Confirm the two published files actually exist in public/vendor/icon-picker/ before debugging anything else — a missing publish is the most common "nothing renders" cause.

Where to look first. The README's Usage → Basic block is the whole contract: wire:model for write-back, :value for initial/redisplay. Everything else (placeholder, disabled, class) is a thin prop. The theming section lists the CSS custom properties you can override; the dark-mode section tells you how to activate the dark palette. If you plan to fork, agents/AGENTS.md maps the repo: src/ is the service provider/icon manager/Blade component, resources/ holds the view, Alpine JS, and CSS, resources/dist/ is the built bundle that gets published.

First use case. Drop one picker into an existing Livewire form and render the chosen icon right next to it so you can see the round-trip:

class CreateMenu extends Component
{
    public string $icon = '';

    public function rules(): array
    {
        return ['icon' => ['required', 'string']];
    }
}
<form wire:submit="save">
    <x-icon-picker::icon-picker wire:model="icon" :value="$icon" placeholder="Choose a menu icon" />
    @error('icon') <p class="text-red-500">{{ $message }}</p> @enderror

    @if ($icon)<x-icon name="{{ $icon }}" class="size-6" />@endif
    <button type="submit">Save</button>
</form>

The persisted string is a blade-icons ID (heroicon-o-home, fa-solid-bell), so it is immediately usable with <x-icon>, @svg(), or svg() in list and show views. That interop is the reason this is a low-regret adoption: swapping the picker later is a UI change, not a data migration.

Implementation Patterns

Wrap the vendor tag once. Do not let product developers call <x-icon-picker::icon-picker> directly. Create <x-app.icon-field> that hard-enforces the dual binding, the error display, the theme class, and your standard :value="old(...)"-style fallback. This single step removes the entire class of "picker silently forgot the saved icon" bugs and lets you swap the implementation later without touching call sites.

Selection vs. binding. This is a Blade component with an Alpine island, not a Livewire component — do not reach for wire:model.live or assume per-keystroke server round-trips. Search, filtering, and lazy chunking happen in the browser; the only server interaction is the model write on selection. Design forms accordingly: validation runs on submit, not on icon hover.

Display outside the form. Because the value is a blade-icons ID, "show the chosen icon" is a one-liner anywhere in the app (<x-icon :name="$menu->icon" />). Wrap that in a small helper/partial that falls back to a placeholder when the string is empty — you will need this in tables and show pages.

Multiple pickers on one page. Give each an unambiguous wire:model key (icon, favicon, badge_icon). Early docs describe the catalog as serialized per page; v1.1.0 introduced AJAX-based loading with set/variant filtering — either way, verify the real cost on a page that hosts several pickers before shipping.

Search-aware labelling. Search is token-substring matching: ar le finds Arrow Left. This rewards consistent pack naming (arrow-left, arrow-right) and punishes abbreviations buried mid-word. If your UX copy promises a search, keep icon set names conventional.

Dark mode / theming. Two supported routes: add ip-dark to the component (or any ancestor) via class, or rely on a [data-theme="dark"] attribute upstream. Bind it to your existing theme store rather than inventing a second toggle. For brand alignment, override the documented custom properties in your own stylesheet scoped to the picker root — do not edit published vendor CSS, because the next vendor:publish overwrite (or a fresh deploy) will wipe it.

Validation. required|string is the minimum. If you want to prevent arbitrary strings, validate against the actual icon catalog rather than trusting the client — the component constrains the UI, not the request payload.

Deployment. Commit the published assets (or run the publish step in your build pipeline). v1.0.2 shipped precisely because missing dist assets broke vendor:publish after install, so treat the published directory as part of your deploy artifact and cache-bust it alongside the rest of your vendor assets.

Pinning and ownership. Pin an exact version, keep a fork-ready note, and budget for dependency bumps when Laravel majors move (the README's window is Laravel 10–12; v1.0.0's release notes say 10–13 — resolve that against your composer.json before upgrading).

Gotchas and Tips

  • The dual-binding footgun. wire:model="icon" alone does not seed the initial value. Omit :value="$icon" and the picker renders blank on an edit form — silently, with no console error. The wrapper partial is the durable fix; a PR-checklist rule is the cheap one.
  • Script order. The JS must load after Alpine. If the grid renders but does nothing on click, check load order before touching the component.
  • Documentation drift will cost you a support ticket. The README states dark mode does not auto-detect prefers-color-scheme and then states it is supported automatically; theming examples reference .icon-picker-root while the dark-mode section uses .ip-root. Open devtools, find the real root class, and write your override against that. Don't copy-paste the docs examples blind.
  • Version drift on loading strategy. README/feature list describes lazy rendering 30 at a time; the v1.1.0 changelog adds AJAX-based loading plus set/variant filtering. Confirm which behaviour your pinned version has, and measure payload and render time on a representative page with your real packs installed — a full Heroicons install is ~1,200 glyphs.
  • No documented config surface. Chunk size (30) is a constant and there is no documented icon-set allow-list. If a project has Heroicons + Font Awesome + a custom pack, you get all of them. The practical lever is limiting installed packs, not configuring the picker.
  • Stale icon IDs. If a pack is renamed or a glyph removed, the stored string may not resolve. Decide now whether an unknown ID renders nothing, throws, or falls back — and test it, because :value holding an unregistered ID is not documented behaviour.
  • CSP. Inline SVG and Alpine expressions commonly need unsafe-inline or a nonce. If your CSP is strict, dry-run the picker on a staging page early; x-html/innerHTML insertion is the usual failure point.
  • Accessibility is claimed, not evidenced. Arrow keys, Enter, and Escape are advertised; ARIA roles, focus trapping, and screen-reader announcement are not documented. Test with VoiceOver/NVDA before shipping to an admin surface, and add an explicit label around the trigger in your wrapper.
  • Asset publish gotcha. Stay on ≥ 1.0.2 (the release that includes compiled dist assets). After any re-publish, re-check the two files — a stale public/vendor/icon-picker/icon-picker.css will make your theming overrides appear to "not work".
  • Ecosystem risk is real but bounded. Zero stars, zero dependents, single maintainer. MIT plus a small surface area means one developer can own a fork; that caps the downside. Plan for ownership and support yourself — pin, commit assets, and keep the internal wrapper as the seam you'd fork behind.
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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings