eminos/statamic-iconify
Integrate Iconify with Statamic via a searchable fieldtype that lets editors browse and select from thousands of open-source icons. Render chosen icons easily in your Antlers templates, with straightforward installation and configuration for flexible, consistent icon usage across your site.
statamic-iconify is a Statamic fieldtype that swaps the plain text field for an Iconify picker in the Control Panel. v2.1.0 is the first release with real governance over which icons editors can pick, so the setup order matters.
Install and publish the config first — before you touch any blueprints:
composer require eminos/statamic-iconify
php artisan vendor:publish --tag=statamic-iconify-config
This drops config/statamic-iconify.php in place. Commit it — it's your site-wide allowlist.
Decide your global envelope in that file:
return [
'allowed_prefixes' => ['mdi', 'tabler', 'heroicons', 'ph'],
'allowed_categories' => [],
'allowed_licenses' => ['MIT', 'Apache 2.0'],
'default_store_as' => 'name',
];
Empty arrays mean "no restriction" — see Gotchas.
Add the field to a blueprint (e.g. resources/blueprints/collections/pages/page.yaml):
handle: hero_icon
field:
type: iconify
display: 'Hero Icon'
First real use case: a "feature card" or "service" collection where each entry needs an icon. Add the field, open an entry, and the search modal appears with your filtered sets only. Save — the value is stored according to default_store_as (typically the icon name like mdi:rocket).
Render it in Antlers/Blade. The release notes mention "the icon tag" — the addon ships a tag/helper for rendering the stored value; check the package README for the exact tag name before wiring it into templates. Because the field returns a plain identifier, {{ hero_icon }} is also usable inside your own SVG helpers.
Where to look first when something is wrong: the published config file, then the field's blueprint YAML, then GET /iconify/config (the CP endpoint added in this release) — it returns exactly what the frontend thinks the rules are.
Lock down globally, narrow per field. The core model in v2.1.0 is inheritance: global config defines the ceiling, each field can only narrow. A clean pattern is a permissive-but-sane global config (your brand's icon families + licenses you can legally ship), then tighten per blueprint:
handle: nav_icon
field:
type: iconify
icon_sets: [mdi, tabler] # subset of allowed_prefixes
category: 'UI 24px'
licenses: [MIT]
Put a comment in every narrowed blueprint pointing back to the config, so the next dev knows why ph is missing from that one field.
Use licenses as a compliance gate, not an aesthetic filter. If legal has signed off on MIT and Apache 2.0 only, set allowed_licenses globally to exactly those strings and leave it there. Editors then physically cannot pick a CC BY-SA icon into a client deliverable. This is the single highest-value thing this release adds.
default_store_as is a schema decision, not a preference. Settle it before content is authored. Storing names (mdi:rocket) keeps entries small and lets you swap rendering later; storing markup makes templates trivial but bloats the content table and freezes the output. If you have existing content, changing this later means a migration over every entry using the field.
Two-tier field strategy. For marketing pages, give editors a broad field (many sets, multiple categories). For UI chrome (nav icons, button icons), create a second field with a narrow set and a fixed category so the result matches your existing icon style. Same fieldtype, different YAML — no custom code.
Build your own picker UI if needed. GET /iconify/config exposes the resolved allowlist to the CP frontend. If you're writing a custom Vue component or a bespoke icon browser, hit that endpoint instead of re-reading the config file server-side — it's guaranteed to match what the field itself enforces.
Localization/multi-site: field config lives in blueprints, which are per-site-configurable. If one site in a multisite install must use a different icon family (e.g. a regional brand), narrow its blueprint rather than branching the global config.
Empty array ≠ nothing allowed. In the example config, 'allowed_categories' => [] clearly means "no category restriction". Assume the same for allowed_prefixes and allowed_licenses — an empty array is the permissive default. Don't ship [] thinking you've locked things down; explicitly list prefixes if the goal is to restrict.
Field config can never widen. This is the release's stated rule and the most common source of "the dropdown is missing my set" confusion. If a field selects heroicons but the global config doesn't include heroicons, the field silently has nothing to offer (or falls back). Always debug top-down: config → endpoint → field.
Dropdowns are API-backed and cached for 24 hours. The set/category/license dropdowns are populated from Iconify's /collections endpoint with a 24h cache. Two consequences: (a) on a fresh install or in restricted-network environments the dropdowns can come up empty — verify outbound access to the Iconify API; (b) after changing your allowlist you won't see new dropdown options instantly, and conversely stale options may linger. Clear the application cache when validating config changes.
License strings must match Iconify's spelling exactly. Apache 2.0 is not Apache-2.0, and CC BY 4.0 has specific spacing. A typo doesn't error — it just filters everything out. Copy the strings verbatim from the Iconify collections response (/iconify/config or the API directly) rather than typing them.
Filtering controls the picker, not the renderer. Narrowing allowed_prefixes doesn't retroactively invalidate content. Existing entries holding fa6-solid:... will keep rendering via the tag even after you remove fa6-solid from the allowlist. That's usually what you want (no broken pages), but it means "removed from config" ≠ "gone from the site." Audit existing content separately if you're doing a licensing cleanup.
Plain-string field values are now safe. v2.1.0 fixed the icon tag crashing when a field value is a plain string rather than the expected structured value. If you were dodging this with {{ if hero_icon }} guards or a custom modifier, you can simplify — but keep the guard if you're on a mixed-version fleet.
Upgrading from 2.0: this is additive (new config, new field options), so no migration is required, but behaviour changes the moment you publish the config. Publishing a restrictive allowed_prefixes immediately narrows every iconify field on the site. Publish, verify against GET /iconify/config, then deploy — don't publish straight into production.
Extension points to know: the published config file is the sanctioned override surface, and default_store_as is the one setting that alters stored data shape. The bundled Pest (PHP) and Vitest (JS) suites mean you can fork and test locally with ./vendor/bin/pest and npm run test if you need behaviour the config doesn't cover — but prefer filtering over forking, since the allowlist is the maintained path.
Cosmetic but worth noting: the v2.1.0 filter badges (purple sets / blue category / green licenses, grouped with +N more tooltips) are your first debugging signal. If an editor reports "wrong icons available," ask them to read the badges back to you — it shows the resolved field config, which immediately tells you whether the problem is global config or blueprint YAML.
How can I help you explore Laravel packages today?