symfony/ux-icons
Render local and remote SVG icons in Twig with the ux_icon() function or the twig:ux:icon component. Pull icons from any registered pack, like mdi:check, and style them inline with classes. Browse the full icon set and live demo at ux.symfony.com/icons.
Architecture fit — Weak on paper, workable with a wrapper. symfony/ux-icons is a Symfony bundle, not a framework-agnostic package: it ships UXIconsBundle (extends Symfony\Component\HttpKernel\Bundle\Bundle), reads config/packages/ux_icons.yaml via the Symfony Config component, registers services in a Symfony DI container, resolves a PSR-6 CacheInterface (defaulting to cache.app), and hooks cache:warmup. Laravel has no bundle/kernel/config-tree concept, so "install and configure" becomes "build an adapter." The genuinely reusable core is the IconRenderer + IconRegistry + Iconify fetch/sanitize pipeline; the Twig-facing surface (ux_icon(), <twig:ux:icon />) is Symfony-UX-specific and does not work in Blade.
Integration feasibility — Three viable paths, in descending order of cost:
symfony/framework-bundle, a minimal Kernel, and a cache-contracts→Laravel-cache adapter. High blast radius for a rendering helper. Not recommended.IconRenderer, IconRegistry, and the Iconify fetcher manually in a Laravel ServiceProvider, point them at a CacheInterface adapter over Laravel's cache, and expose a Blade component <x-icon name="mdi:check" /> plus a @icon(...) directive. Cost is roughly 2–4 weeks including tests; accept coupling to classes that may be marked @internal and pin exact versions.blade-ui-kit/blade-icons. Loses the sanitizer and Iconify-on-demand story but avoids all adapter risk.Caveat if the team wants Twig in Laravel: rcrowe/twigbridge gives Twig, but Twig Components (<twig:ux:icon />) additionally need symfony/ux-twig-component runtime machinery — treat the component syntax as out of scope; standardize on the function or a Blade component.
Technical risk
>=3.2.0 (or >=2.36.1) as a hard gate and require composer audit in CI. Note the fix keeps <style> for theming and strips event handlers, CDATA, PI, javascript:/data:text/html hrefs — verify these semantics match your CSP.symfony/* constraints. Resolve this in a spike before committing.symfony/ux monorepo, not adoption. Real usage, issues, and PRs live upstream. Do not use these metrics in a build/buy scorecard; instead assess Symfony core-team backing and release cadence (steady, with prompt security backports to 2.x).xmlns to downloaded icons, explicitly breaking tests that assert on ux_icon() output. Any snapshot/HTML-diff test suite must be attribute-order tolerant.Key questions
<svg> with <style> blocks comply?Stack fit — Poor-native, moderate-with-work. Laravel's rendering path is Blade + Illuminate\View; this package's value is Twig-shaped. The realistic integration is a thin Laravel package we own (acme/laravel-icons) that depends on symfony/ux-icons internals and exposes idiomatic Blade ergonomics: <x-icon name="mdi:check" class="w-4 h-4" />, a @icon directive, and config/icons.php mirroring the upstream options (default_icon_set, icon_sets, aliases, iconify.auto_lock, ignore_not_found).
Migration path
prefix:name identifiers. This inventory is the migration backlog and the license-review list.iconify on-demand off in production. This converts a network dependency into a file read.Compatibility
symfony/http-client for remote fetch (2.25.0 improved DX when absent) — irrelevant once icons are locked, so locking removes a whole dependency.class, fill, aria-*) pass through cleanly; int/float attribute support (2.20.0) covers width/height/stroke-width.Sequencing
>=3.2.0, run composer audit, clear the icon cache post-upgrade so cached icons are re-sanitized, clear icon-set licenses.IconRenderer boots in a Laravel ServiceProvider with a PSR-6 adapter over CacheRepository; measure render cost for a 60-icon page. Kill criterion: unresolvable Symfony/Laravel dependency conflict → fall back to blade-ui-kit.ignore_not_found: true in prod, false in CI so missing icons fail the build).auto_lock in dev only, add icons:lock/icons:clear Artisan commands wrapping upstream.cache:warmup hook is wired to Laravel's optimize/artisan bootstrapping.Maintenance — Two maintenance surfaces: (a) upstream symfony/ux-icons, actively maintained by the Symfony core team with a clean security-backport record to 2.x; (b) our adapter, which is the real cost — it depends on non-public API shape and will break on minor releases. Budget ~1–2 engineer-days per upstream minor release for verification, more for majors (3.0 dropped PHP 8.3 support outright). Pin exact versions, upgrade deliberately, and keep an integration test suite as the upgrade gate. Note the repo is a read-only split: issues and PRs go to symfony/ux, so we cannot patch in place — we'd maintain a fork or upstream PRs.
Support — No vendor SLA (MIT, community). Internal support burden is "who understands Symfony DI and the sanitizer?" — realistically 1–2 named owners. Document the sanitizer's allowlist behavior for the security team, since it is the control preventing stored XSS from third-party SVG. Also document that clearing the icon cache is mandatory after any upgrade touching sanitization, or stale unsanitized output persists.
Scaling
<symbol> sprite + <use>. Decide this at design-system level, not per feature.Failure modes
| Mode | Symptom | Mitigation |
|---|---|---|
| Missing icon | Render-time exception, 500 on a page | ignore_not_found: true in prod; CI fails loudly |
| Iconify unreachable | Slow/failed render if on-demand enabled | Lock icons; auto_lock for dev; circuit-break the fetcher |
| Cold cache / stampede | p95 spike after deploy | cache:warmup in pipeline; pre-warm the locked set |
| Stale unsanitized cache | XSS persists post-upgrade | Mandatory cache clear in upgrade runbook |
| Upstream minor break | Adapter fatal after composer update |
Exact pins + integration tests + staged upgrades |
| Test suite churn | Red builds on attribute changes | Attribute-order-tolerant assertions; avoid raw HTML equality |
| License exposure | Legal takedown of an icon set | Per-set license register before adoption |
| Blade/Twig drift | Two icon systems emerge | One canonical component; ban raw <svg> in review |
Ramp-up — Low for consumers, moderate for maintainers. A Blade developer needs ~30 minutes to learn <x-icon name="prefix:name" class="…" /> and the currentColor styling convention. The adapter owner needs working knowledge of Symfony DI, service tags, config trees, and PSR-6 cache contracts — 2–3 days of focused reading plus the spike. Designers/PMs need one artifact: the icon catalog (prefix:name → license → owner), which doubles as the migration checklist and the compliance record.
Recommendation — Approve a one-week spike only, gated on (a) dependency resolution in our Laravel version and (b) license clearance for the target icon sets. If the spike fails, adopt blade-ui-kit/blade-icons and port the sanitizer concept rather than the code. If it succeeds, adopt with locked icons, on-demand fetching disabled in production, and >=3.2.0 pinned, and scope the first release to a single canary surface before any Font Awesome retirement.
How can I help you explore Laravel packages today?