PHPackages                             raise-studio/filament-icon-picker - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. raise-studio/filament-icon-picker

ActiveLibrary

raise-studio/filament-icon-picker
=================================

Self-developed, dependency-free Filament form field for picking Heroicons. Zero per-icon server roundtrips, modal grid, recents &amp; favorites, keyboard-first, WCAG 2.1 AA.

v0.1.0(yesterday)02↑2900%MITPHPPHP ^8.2

Since Jul 31Pushed yesterdayCompare

[ Source](https://github.com/raise-studio/filament-icon-picker)[ Packagist](https://packagist.org/packages/raise-studio/filament-icon-picker)[ RSS](/packages/raise-studio-filament-icon-picker/feed)WikiDiscussions master Synced today

READMEChangelogDependencies (5)Versions (2)Used By (0)

filament-icon-picker
====================

[](#filament-icon-picker)

📘 中文文档： [readme.zh-CN.md](readme.zh-CN.md)

> A **self-built, zero extra front-end dependency** Filament form icon-picker field component. One-shot SVG map delivery · modal grid · recents / favorites · keyboard-first · multi-icon-set extensibility · accessibility-friendly.

Provides a handy icon picker inside any Filament form: a styled input triggers a standalone modal with a large icon grid, live search, recents and favorites sections, style switching, and keyboard navigation, plus support for plugging in any third-party or custom icon set.

---

1. Overview
-----------

[](#1-overview)

`filament-icon-picker` is a **Filament form field component package** that does exactly one thing well: provide icon-selection capability inside a form.

- **Zero extra front-end dependencies**: Interaction logic is built on Alpine (bundled with Livewire); assets are registered through `FilamentAsset` — no npm package required.
- **Performance-first**: When the modal opens, the entire `name → inline-SVG` map is fetched **in a single request**; all subsequent rendering, search, and style switching happen client-side with no per-icon server round-trips.
- **Multi-icon-set architecture**: Ships with Heroicons (Outline / Solid styles) and natively supports plugging in any third-party / custom icon set side-by-side (e.g. Feather, Lucide, or your own SVGs).
- **Works out of the box**: Auto-discovery of directory-based icon sets, `localStorage`-backed recents / favorites, light &amp; dark theme adaptation.

---

2. Features
-----------

[](#2-features)

- **Modal grid picker**: A popup independent of the input, a spacious responsive `N`-column grid — no more cramped dropdowns.
- **Live search**: Filter as you type, with exact-prefix matches ranked first (150ms debounce by default).
- **Recents + favorites**: Persisted to `localStorage`, pinned at the top, retained across sessions.
- **Style switching**: When the current set supports multiple styles, switch (e.g. Outline / Solid) and preview live inside the modal.
- **Keyboard-first**: `↑ ↓ ← →` move focus through the grid, `Enter` selects, `Esc` closes, typing jumps to the match.
- **Accessibility**: Focus trap + ARIA `dialog` / `tablist` / `grid` semantics, WCAG 2.1 AA compliant.
- **One-click copy**: Copy the full class name of the current icon to the clipboard.
- **Multi-icon-set**: The icon-set switcher bar appears on demand, supporting Heroicons alongside any custom set.
- **Zero-cost extension**: Drop a folder to plug in, register in one line of code, or implement a contract to take over dynamic / font / remote icons.
- **Safe inlining**: Third-party SVGs are sanitized by the built-in `SvgSanitizer` before inlining — strips scripts and event-attribute risks.
- **Visual polish**: Glassmorphic modal, magnetic hover, smooth transitions, automatically following the panel's light / dark theme.

---

3. Requirements
---------------

[](#3-requirements)

DependencyVersionPHP`^8.2`Laravel`^12` (required by Filament 4)Filament`^4``spatie/laravel-package-tools``^1.16`- Livewire and Alpine ship with Filament 4 — **no separate installation required**.
- Built-in icon SVG resources are published with the package — **no host-side icon component package needed**.
- Zero additional npm dependencies.

---

4. Installation
---------------

[](#4-installation)

### 4.1 Install via Composer

[](#41-install-via-composer)

```
composer require raise-studio/filament-icon-picker
```

The service provider is already declared in `composer.json`'s `extra.laravel.providers`. After Composer installs it, **it auto-registers** — no manual plugin or service-provider wiring needed.

### 4.2 Publish front-end assets

[](#42-publish-front-end-assets)

JS / CSS are registered via `FilamentAsset` and must be published once into `public/` to be loadable by the browser:

```
php artisan filament:assets
```

> During development, if you use a path repository + symlink, you still need to re-run the command above after asset changes and hard-refresh the browser (`Ctrl + F5`).

### 4.3 Development (path repository, optional)

[](#43-development-path-repository-optional)

Add a path repository with `symlink` to the host `composer.json`:

```
{
    "require": {
        "raise-studio/filament-icon-picker": "^0.1.0",
        "php": "^8.2"
    },
    "repositories": [
        {
            "type": "path",
            "url": "../packages/icon-picker",
            "options": { "symlink": true }
        }
    ]
}
```

---

5. Basic Usage
--------------

[](#5-basic-usage)

Use the field class inside any Filament Schema / Form:

```
use RaiseStudio\IconPicker\Forms\Components\HeroiconPicker;

HeroiconPicker::make('icon')
    ->label('Icon')
    ->allowStyleToggle()   // allow Outline / Solid switching (on by default)
    ->showRecents()        // show recents (on by default)
    ->showFavorites()      // show favorites (on by default)
    ->placeholder('Select an icon');
```

The field extends `Filament\Forms\Components\Field`, so all common methods (`label` / `placeholder` / `required` / `visible` / `helperText` …) are available.

**Storage convention**: The field always saves a **fully-qualified string**. The built-in Heroicons set is saved as `heroicon-o-xxx` / `heroicon-s-xxx`; a custom icon set is saved in `::` format (see Chapter 7). Style switching only changes the `o` / `s` prefix or the within-set style — it never loses the full class name, making it easy for the rendering side to fetch the SVG by full name.

---

6. API Reference &amp; Property List
------------------------------------

[](#6-api-reference--property-list)

### 6.1 Field configuration methods

[](#61-field-configuration-methods)

`HeroiconPicker` provides the following methods on top of `Field`:

MethodDefaultDescription`->style(string $style)``'outline'`Initial style: `outline` | `solid` (applies to built-in Heroicons only)`->allowStyleToggle(bool $v = true)``true`Whether to show the style switcher in the modal (only effective when the current set supports multiple styles)`->showRecents(bool $v = true)``true`Whether to show the "Recents" section`->showFavorites(bool $v = true)``true`Whether to show the "Favorites" section`->recentLimit(int $n)``12`Number of recents retained (minimum 1)`->gridColumns(int $n)``8`Modal grid columns (responsive, minimum 2)`->searchDebounce(int $ms)``150`Search debounce in ms (minimum 0)### 6.2 Field state convention

[](#62-field-state-convention)

- The database column should be `VARCHAR` holding the full class-name string — **no schema change, zero migrations**.
- Built-in set: `heroicon--`, e.g. `heroicon-o-document`, `heroicon-s-folder`.
- Custom set: `::`, e.g. `feather:default:home`, `custom:default:star`.

### 6.3 Consuming the selected icon on the render side

[](#63-consuming-the-selected-icon-on-the-render-side)

The component only produces / back-fills the class name; rendering is left to the consumer. Fetch the inline SVG by full name:

```
use RaiseStudio\IconPicker\Icons\IconManifestContract;

$svg = app(IconManifestContract::class)->svg($record->icon);
// $record->icon can be heroicon-o-document (built-in) or feather:default:home (custom set)
```

`IconManifest::svg()` automatically routes the full name to the correct icon set and returns the inline SVG; it returns `null` when not found.

---

7. Customizing Third-Party Icon Sets (key section)
--------------------------------------------------

[](#7-customizing-third-party-icon-sets-key-section)

The component natively supports plugging in **any third-party / custom icon set** alongside the built-in Heroicons. The "Icon Set" switcher bar at the top of the modal appears automatically as sets are registered; storage uniformly uses the `set:style:name` format, fully compatible with the built-in Heroicons data.

### 7.1 Core concepts &amp; storage format

[](#71-core-concepts--storage-format)

- **Set**: An icon library identified by a unique `key` (e.g. `heroicons` / `feather` / `custom`), used as the routing and storage prefix.
- **Style**: A variant dimension within a set. Heroicons uses `outline` / `solid`; a single-style set typically uses `default`.
- **Fully-qualified name**: `::`, e.g. `feather:default:home`, `brand:default:logo-a`.
- Built-in Heroicons keep the legacy `heroicon-o-xxx` format **for backward compatibility**; both can coexist without interference.

### 7.2 Method 1: Directory auto-discovery (zero code, recommended for pure SVG libraries)

[](#72-method-1-directory-auto-discovery-zero-code-recommended-for-pure-svg-libraries)

`IconManifest` automatically scans the **top-level subdirectories of the host application's `resources/icons/` directory** at construction time: each subdirectory is auto-registered as a `DirectoryIconSet`, and the modal switcher bar immediately shows that set — **drop an SVG folder into your own project and it's plugged in, no PHP required**.

> ⚠️ **Only put extension icon libraries into your own application directory — never into the composer-installed `vendor/.../filament-icon-picker/resources/icons/`.** That directory belongs to the package itself and gets overwritten by `composer update`, so any changes there are guaranteed to be lost.

The default scan path is the application `resource_path('icons')` (i.e. `/resources/icons/`). It can be overridden via the `icon-picker.discover_path` config (absolute paths supported). This config ships in `config/icon-picker.php`; publish it with `php artisan vendor:publish --tag=filament-icon-picker-config` and edit as needed.

Directory convention (the directory structure is the configuration):

```
/resources/icons/
├── feather/                  ← auto-discovered: flat *.svg → single style "default"
│   └── home.svg  user.svg …
├── bootstrap/                ← multi-style: subfolder name is the style
│   ├── outline/*.svg
│   └── solid/*.svg
└── /           ← drop it in and it's plugged in
    └── *.svg (or split into styles via outline/ solid/ subfolders)

```

> The built-in Heroicons SVGs live in the package's own `resources/icons/` root (read by a dedicated class, not part of auto-discovery). This is package-owned resource — please do not modify or depend on its path.

Rules:

- A directory containing `*.svg` directly (flat) → single style, style name is `default`;
- A directory that also contains subfolders (each holding `*.svg`) → subfolder name is the style (e.g. `outline` / `solid`);
- Optional `meta.json` to override display name / style list / default style: ```
    { "label": "Lucide", "styles": ["outline"], "defaultStyle": "outline" }
    ```
- The directory name is auto-converted to a human-readable label (`lucide` → `Lucide`, `brand-icons` → `Brand Icons`).

**Replace / override an existing set**: Calling `register()` with the same `key` overrides the original set (see 7.3). For example, to replace the built-in Heroicons entirely, register your own `DirectoryIconSet` with key `heroicons`; **do not** override by dropping same-named files into the vendor directory.

### 7.3 Method 2: One-line registration (host-directory SVG library)

[](#73-method-2-one-line-registration-host-directory-svg-library)

When the icon library lives in a **host project directory** (rather than inside the package), use `DirectoryIconSet` to register it in one line inside the host `AppServiceProvider::boot()`:

```
use RaiseStudio\IconPicker\Icons\DirectoryIconSet;
use RaiseStudio\IconPicker\Icons\IconManifestContract;

public function boot(): void
{
    $manifest = app(IconManifestContract::class);

    $manifest->register(new DirectoryIconSet(
        'custom',                            // key: storage prefix, e.g. custom:default:xxx
        'Custom',                            // label: display name on the switcher bar
        base_path('resources/icons/custom'), // SVG directory (flat *.svg is fine)
        ['default'],                         // styles: style list; pass ['outline','solid'] for multi-style with subfolders
        'default',                           // defaultStyle: default style (optional, defaults to first of styles)
        true                                 // sanitize: whether to sanitize SVG (default true)
    ));
}
```

The directory convention is the same as Method 1 (flat = single style `default`, subfolder name = style), with optional `meta.json` overriding label / styles.

#### `DirectoryIconSet` constructor parameters

[](#directoryiconset-constructor-parameters)

ParameterTypeRequiredDefaultDescription`$key`stringyes—Unique identifier; used as storage prefix and routing. Do not duplicate the built-in `heroicons` or an already-registered set (duplicate = override)`$label`stringyes—Display name on the switcher bar; can also be overridden by `meta.json``$basePath`stringyes—Absolute / base path of the SVG directory; flat or subfolder-split styles both work`$styles`?arrayno`null`Style list; when `null`, auto-detected (subfolders → subfolder names, otherwise `['default']`)`$defaultStyle`?stringno`null`Default style; when `null`, takes the first of `$styles``$sanitize`boolno`true`Whether to sanitize third-party SVGs via `SvgSanitizer` (built-in Heroicons are not sanitized, trusted)### 7.4 Method 3: Implement the contract (dynamically generated / font classes / remote fetch)

[](#74-method-3-implement-the-contract-dynamically-generated--font-classes--remote-fetch)

When icons are not "a bunch of `.svg` files in a directory" (inlined-in-code, programmatically generated, remotely fetched, or font-class icons needing custom rendering), implement `IconSetContract` and register it in the host `boot()`:

MethodReturnsDescription`key(): string`stringUnique identifier (storage prefix, routing)`label(): string`stringDisplay name on the switcher bar`styles(): array`arrayStyle list; single-style returns `['default']``defaultStyle(): ?string`?stringDefault style; returns `null` when there is no style dimension`names(string $style): array`arrayBare icon-name list for a given style (without prefix)`svgMap(string $style): array`array`bare-name => inline-SVG`, delivered all at once via the icon map`svg(string $fullOrName, ?string $style = null): ?string`?stringResolve and return the inline SVG; return `null` when not found. Accepts both the new `set:style:name` format and bare-name + style```
use RaiseStudio\IconPicker\Icons\IconSetContract;

class BrandIconSet implements IconSetContract
{
    public function key(): string { return 'brand'; }
    public function label(): string { return 'Brand'; }
    public function styles(): array { return ['default']; }
    public function defaultStyle(): ?string { return 'default'; }

    public function names(string $style): array { return ['logo-a', 'logo-b']; }

    public function svgMap(string $style): array {
        return [
            'logo-a' => '...',
            'logo-b' => '...',
        ];
    }

    public function svg(string $fullOrName, ?string $style = null): ?string {
        if ($style === null && preg_match('/^brand:default:(.+)$/', $fullOrName, $m)) {
            $name = $m[1];
        } else {
            $name = $fullOrName;
        }
        return $this->icons[$name] ?? null;
    }
}
```

Register (also in host `boot()`): `$manifest->register(new \App\Icons\BrandIconSet());`

> Registration note: `IconManifest` is bound as a `singleton`. The custom set you `register()` in host `boot()` resolves to the **same instance** that the field view data and the icon-map controller use, so nothing is lost.

### 7.5 Safe sanitization &amp; cache refresh

[](#75-safe-sanitization--cache-refresh)

- **Safe sanitization**: Third-party SVGs are inlined via `x-html`, and by default pass through `SvgSanitizer::clean()`, stripping ``, ``, `on*` event attributes, and dangerous URIs like `javascript:` / `data:text/html`. Built-in Heroicons go through a dedicated trusted class and are not sanitized. For untrusted / complex SVGs, consider a more complete HTML sanitization solution.
- **Caching**: Each set's `names()` / `svgMap()` are cached via `Cache::rememberForever`.
    - **Add an entire new library directory** (Method 1) → auto-discovered, no cache clearing needed;
    - **Append / modify SVGs in an existing directory** (Method 1 / 2) → `php artisan cache:clear` to refresh;
    - **Method 3, changed code** → `php artisan optimize:clear` (or restart the service) to pick up the new class;
    - After changing package JS / CSS, you must re-run `php artisan filament:assets` and hard-refresh the browser.

### 7.6 Refreshing built-in Heroicons assets

[](#76-refreshing-built-in-heroicons-assets)

The built-in Heroicons SVGs ship with the package. To refresh / regenerate the built-in assets:

```
php artisan filament-icon-picker:sync
```

---

8. Assets, Routes &amp; Visuals
-------------------------------

[](#8-assets-routes--visuals)

### 8.1 Route endpoints

[](#81-route-endpoints)

The component registers two GET endpoints (both called by the front-end on demand):

EndpointPurpose`/filament-icon-picker/icon-map?set=&style=`Delivers a set's `name => inline-SVG` map for a given style in one request (single request, then purely client-side)`/filament-icon-picker/sets`Returns the available icon-set list (switcher data source; currently also injected directly via field view data to avoid async failure)### 8.2 Front-end architecture notes

[](#82-front-end-architecture-notes)

- **One-shot SVG map**: On modal open, `fetch` the icon map and store it in Alpine state; all subsequent rendering / search / style switching happens client-side with zero server round-trips.
- **Glassmorphic modal**: `backdrop-filter` blur + semi-transparent border, 20px rounded corners.
- **Magnetic hover**: `transition: transform .3s cubic-bezier(.16,1,.3,1)` + `:hover { transform: scale(1.05) translateY(-2px) }`.
- **Light / dark adaptation**: Colors reference Filament panel CSS variables, automatically following light / dark / system themes with smooth transitions.
- **Scoped**: Component styles are uniformly namespaced under the `.fi-icon-picker-*` prefix to avoid polluting the global scope.

---

9. FAQ
------

[](#9-faq)

**Q: Is it compatible with existing data?**A: Fully compatible. The built-in set reads/writes the `heroicon-o-xxx` full name; custom sets use the `set:style:name` format. The two can coexist with zero migration.

**Q: Can it support icon sets beyond the built-in Heroicons?**A: Natively and at zero cost. A pure SVG library is auto-registered by dropping it into the **host application's** `resources/icons//` (scans the app's `resource_path('icons')` by default, configurable via `icon-picker.discover_path`); a host-directory SVG registers in one line via `DirectoryIconSet`; dynamic / font / remote icons implement `IconSetContract` then `register()`. Storage is unified as `set:style:name`, and third-party SVGs are sanitized before inlining by default.

**Q: Why a modal instead of a dropdown + icon preview?**A: With 300+ icons, a dropdown is cramped and the search experience is poor; an independent modal grid is significantly better for preview and retrieval, with zero extra dependencies.

**Q: The front-end didn't update after I changed a custom icon set?**A: Appending / modifying SVGs in an existing directory needs `php artisan cache:clear`; changing JS / CSS needs `php artisan filament:assets` plus a browser hard-refresh.

---

> Document status: multi-icon-set + modal + set switcher bar + style switching + recents / favorites + keyboard navigation + ARIA + directory auto-discovery + contract extension are all implemented. The three ways to integrate a custom icon set are covered in Chapter 7.

###  Health Score

37

—

LowBetter than 81% of packages

Maintenance100

Actively maintained with recent releases

Popularity3

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity35

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

1d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/46275694?v=4)[tianby@shu.edu.cn](/maintainers/Tianby)[@tianby](https://github.com/tianby)

---

Top Contributors

[![raiseinfo](https://avatars.githubusercontent.com/u/22998521?v=4)](https://github.com/raiseinfo "raiseinfo (8 commits)")

---

Tags

laravelHeroiconsfilamenticon-pickerform-field

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/raise-studio-filament-icon-picker/health.svg)

```
[![Health](https://phpackages.com/badges/raise-studio-filament-icon-picker/health.svg)](https://phpackages.com/packages/raise-studio-filament-icon-picker)
```

###  Alternatives

[stephenjude/filament-two-factor-authentication

Filament Two Factor Authentication: Google 2FA + Passkey Authentication

86240.3k9](/packages/stephenjude-filament-two-factor-authentication)[awcodes/filament-curator

A media picker plugin for FilamentPHP.

440384.7k25](/packages/awcodes-filament-curator)[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3914.8k](/packages/rawilk-profile-filament-plugin)[backstage/mails

View logged mails and events in a beautiful Filament UI.

16321.5k](/packages/backstage-mails)[marcelweidum/filament-passkeys

Use passkeys in your filamentphp app

6758.2k2](/packages/marcelweidum-filament-passkeys)[stephenjude/filament-jetstream

A Laravel starter kit built with Filament inspired by Jetstream.

17861.9k3](/packages/stephenjude-filament-jetstream)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
