codeat3/blade-gravity-ui-icons
A Laravel Blade component package for Gravity UI icons, letting you render any icon with simple x-icon tags in your Blade views. No SVGs to copy, no config—just clean, inline icon components for your PHP app.
Architecture fit
The package is a thin adapter over blade-ui-kit/blade-icons; its entire functional payload is a directory of SVG files plus a service provider that registers the gravity-ui-icon-* component namespace. For a TPM, that is the key architectural fact: this is a presentation-layer dependency with no runtime services, no database surface, no queue workers, no external API calls, and no JavaScript bundle contribution. It sits alongside Tailwind, Livewire, and Blade UI Kit without competing for any of the same seams.
The consequential architectural question is not "does it fit the stack" — it does, trivially — but "does it fit the product's design system?" Adopting this package is a design commitment encoded in composer.json. It says "our UI uses Gravity UI iconography" and makes that enforceable in code review rather than a matter of individual taste. That is the actual value proposition, and it should be the framing in a build-vs-buy conversation with design leadership.
Two fit boundaries worth stating explicitly in the decision record:
@gravity-ui/icons JS package — a second icon dependency with a separate upgrade cadence and a visual-parity obligation. Scope this per-surface, not per-product, before committing.fill="currentColor". Any design spec calling for stroked/outlined icons is a different icon family, not a CSS override. Surface this to design early — it is the single most likely source of a mid-sprint "these don't look right" escalation.Integration feasibility
Extremely low effort. One composer require, no service provider registration (auto-discovery), no asset compilation, no build step. A single engineer can be productive in under an hour; the first icon renders from a one-line Blade tag. There is no configuration surface to speak of unless the team needs to rename the component prefix or change a default class.
The integration work that does exist is discretionary and organizational rather than technical:
<x-ui.icon>) so sizing and aria-hidden policy live in one file instead of being ad-hoc across views. This is the highest-leverage hour of work in the whole adoption and should be scheduled, not left to whoever touches the first view.Technical risk
Low and overwhelmingly benign. MIT license, no transitive dependency of weight beyond Blade Icons (already present in most Laravel stacks), and a payload that is literally static SVG. The worst realistic failure is a missing icon rendering nothing — a silent, cosmetic degradation, not an outage.
The real risk is vendor risk, and it is not negligible: zero stars, zero dependents, a single maintainer, and a heavily automated release cadence. The changelog entry for 1.4.0 is a one-line Laravel 13 compatibility bump. That cadence is actually a positive signal — it tracks framework majors — but it also means the package is machine-maintained infrastructure sitting in a load-bearing position.
The mitigation is unusually cheap and should be documented before adoption, not after: copy vendor/.../resources/svg/ into resources/svg/gravity-ui/, register the set in a small IconServiceProvider (~50 lines), and composer remove. Ten minutes, and the dependency is gone. A TPM should require this contingency be written down and estimated, so that "the maintainer disappears" is a known, budgeted response rather than a scramble. It also means the decision is reversible — which is what makes it a low-stakes decision to make.
Other risks worth a line each in the risk register:
codeat3/blade-*-icons family and will surface as "the icon is broken" support noise if the team isn't briefed. The debugging habit is ls vendor/.../resources/svg, and it belongs in onboarding docs.icons:cache produces a compiled PHP file that goes stale on every version bump — new icons don't appear, removed icons keep rendering. The deployment script must run icons:clear && icons:cache unconditionally. This is a recurring Friday-afternoon time-sink if not baked in.resources/views/components/. App components win, silently, producing confusing renders. Low probability, high confusion.aria-hidden="true") or inside something labelled. That is a team policy decision, not a package feature, and it is the most likely source of an accessibility audit finding.Key questions
resources/svg/ directory or the README table? (Answer: the directory. The README drifts.)Stack fit
Presumes Laravel with Blade, and a CSS strategy where color inherits via currentColor. Tailwind is the natural pairing — text-* for color, w-*/h-* for size — but any utility or hand-written CSS using currentColor works identically. Livewire views work fine, with the caveat that each Livewire re-render re-resolves the component; icons:cache makes that cheap. Blade UI Kit and Flux coexist without conflict (different prefixes), though on older Laravel versions the total registered component count is worth a glance.
The one hard boundary: Inertia's root Blade template (layout chrome, <html> shell) is fine; Inertia's Vue/React components are not. If the product is Inertia-heavy, plan for the upstream JS package on the client side and accept that the two icon sources must be kept visually aligned.
Migration path
The realistic starting state is hand-pasted <svg> blobs in Blade files, or a hand-maintained resources/views/components/icons/ directory. Migration is mechanical and incremental — there is no flag day, no dual-write, no data migration. The package can be introduced alongside existing SVGs and views converted opportunistically.
A sensible sequence:
composer require codeat3/blade-gravity-ui-icons on a branch.gravity-ui-icon-* is the pattern, but confirm.<x-ui.icon name size />) with a size scale and a default aria-hidden policy. This is the durable artifact; the package is replaceable, the wrapper is not.vendor:publish tag rather than reproducing it from memory.Teams should expect to find gaps: Gravity UI's catalog will not cover every icon the product currently uses. Those get either an approximation, a retained custom SVG, or a design conversation. Budget for that discovery explicitly rather than assuming 100% coverage.
Compatibility
1.4.0 adds Laravel 13 compatibility, which confirms the package tracks framework majors — a good sign for a dependency in a presentation layer. Pin to a minor (^1.4) and let the lock file do its job; do not chase patch releases unless a specific icon is needed, because the diff between working and broken in this package family is often just a regenerated SVG directory.
Note the dual dependency question if Inertia/Vue surfaces exist: the PHP package and the upstream @gravity-ui/icons JS package version independently. Keeping their visual output aligned is a small but real ongoing obligation, and it is nobody's job unless someone is named.
Sequencing
The dependency install is minutes. The real sequencing constraint is organizational: design ratification → wrapper component → representative conversion → design review → breadth rollout. Compressing steps 1 and 3 (i.e., letting dozens of views adopt raw <x-gravity-ui-icon-*> calls before the wrapper exists) reintroduces exactly the fragmentation the package was adopted to eliminate, and unwinding it later is a mechanical but tedious refactor across every view.
Parallelizable track: deployment script changes (icons:clear && icons:cache, view:cache, config:cache) and the onboarding doc covering the silent-failure debugging habit. Both are independent of the rollout and should land early.
Maintenance
Steady-state maintenance is near zero: no configuration to tend, no assets to compile, no runtime to monitor. The recurring obligations are three, all small:
icons:clear before icons:cache in the deploy pipeline. Bake it in unconditionally — consistent caching beats selective caching someone has to remember.<x-ui.icon> component is the actual design system surface. It needs an owner, and changes to the size scale or default ARIA policy should route through design review, not accrete silently.The maintenance risk concentrated in one place is the single-maintainer upstream. The documented exit — vendor the SVGs, register the set, remove the package — should be written into the ADR with an estimate, so a future maintainer isn't reverse-engineering intent. From that point forward, adding an icon is a cp with no Composer release cadence in the loop.
Support
Support burden is dominated by two predictable, non-obvious failure modes, both of which are documentation problems rather than engineering problems:
ls vendor/.../resources/svg | grep <name>, then optimize:clear, then checking bootstrap/cache/packages.php. Putting this in a runbook removes the most common ticket class.stroke-* or fill-none applied to a filled glyph. Color is text-*; fill-none can blank the icon entirely.Neither requires the package maintainer. Both require ten minutes of onboarding documentation. A TPM should treat that doc as a deliverable, not an afterthought — it is the difference between a self-service dependency and a recurring interruption.
Scaling
Scales linearly and cheaply for the common case. For icon-heavy surfaces — data tables with per-row actions, notification feeds, settings panes — php artisan icons:cache collapses per-request filesystem reads into a single array lookup, which is the difference between hundreds of file reads and one. On a page with three icons it's noise; on a page with two hundred it's real. Add it to the deploy script regardless.
Livewire re-renders re-resolve the component on every update; icons:cache keeps that cheap. The @svg directive skips component class instantiation and is measurably faster in tight loops — the guidance is components in normal Blade for readability, @svg in loops and generated markup. The svg() helper covers PHP-side rendering (queued mail bodies, table cell arrays, PDF templates) and is the escape hatch when markup is built outside a view.
Two scaling caveats: a custom Svg subclass used for global post-processing forfeits the icons:cache benefit — do transformations in the wrapper component instead. And user-supplied icon names are an injection surface; validate against an allowlist or a backed enum before the pattern spreads. Both of these are cheap to get right early and annoying to retrofit.
Failure modes
Ordered by likelihood and blast radius:
| Failure | Likelihood | Blast radius | Response |
|---|---|---|---|
| Wrong icon name, renders nothing | High | Cosmetic | ls the svg directory; validate names |
Stale icons:cache after upgrade |
High (per upgrade) | Cosmetic, confusing | icons:clear && icons:cache in deploy |
stroke-* / fill-none on filled glyph |
Medium | Cosmetic | Brief the team; color via text-* |
| Package discovery cache stale post-install | Medium (first install) | Silent no-op | optimize:clear, dump-autoload |
| Prefix collision with app component | Low | Confusing render | grep -r "gravity-ui-icon" resources/views/; rename prefix |
| Accessibility gap (unlabelled icon-only button) | Medium | Audit finding | Wrapper default + review policy |
| Upstream maintainer goes dark | Low, slow-burn | None short-term; blocked upgrades long-term | Documented 10-minute vendor-out |
| User-supplied icon name probing | Low | Minor info leak, noise | Allowlist / enum |
No failure mode here is an outage. The worst case is a Friday afternoon lost to a stale cache, or an accessibility finding at audit time. Both are preventable with documentation and one deploy-script line.
Ramp-up
Measured in hours, not days. A competent Laravel engineer is productive after reading the README's first example. The ramp-up that actually matters is convention, and it should be delivered as three artifacts:
stroke-*/fill-none trap, the cache-clear-on-upgrade rule, and the prefix-collision check.Total onboarding cost across a small team: under a day, including the wrapper. The package itself teaches nothing new — it is a Composer install and a Blade tag. What takes longer, and is worth the TPM's attention, is the organizational adoption: getting design to ratify the icon set, getting the wrapper reviewed, and getting one representative view converted and signed off before breadth rollout. That is a two-week sequencing exercise wrapped around a one-hour technical install, and treating it as the latter is the most common way this kind of adoption quietly produces fifty slightly different icon call sites.
How can I help you explore Laravel packages today?