niladam/quick-links
Quick Links is a Laravel package for defining and rendering configurable shortcut links in your app. Register links, customize labels and URLs, and output them via Blade components—handy for dashboards, menus, and admin panels.
Architecture fit: niladam/quick-links is a small, single-purpose Filament plugin that injects link buttons into a table header. Architecturally it is a thin render-layer extension: no service providers of consequence beyond Filament plugin registration, no persistence, no jobs, no middleware. Fit is therefore narrow but clean — it suits admin panels built on Filament v3/v4/v5 where operators want quick outbound links (docs, exports, external tools) colocated with a resource table. It is not a candidate for core domain logic and should be treated as presentation-layer decoration only.
Integration feasibility: Feasible and low-effort. The package is Composer-installable, declares filament/tables ^3.0|^4.0|^5.0 as an explicit dependency in v2 (previously an undeclared import — a genuine correctness fix), and now supports Filament v3, v4 and v5 concurrently. Two integration surfaces exist: the PHP builder API (buildResourceLinks(), buildFileLinks(), build()) and a publishable Blade view (vendor:publish --tag=quick-links-views) for markup customisation. The Blade extraction materially improves feasibility for teams whose design system will not accept upstream markup.
Technical risk: Moderate, concentrated in the v2 breaking changes rather than in runtime behaviour.
buildResourceLinks() and buildFileLinks() no longer return HTML strings but ['url' => ..., 'label' => ...] arrays; build() returns a View (?Htmlable) rather than ?HtmlString. Any consumer that echoed, concatenated, or regex-tested these return values breaks at runtime, possibly silently if the value is coerced to string.disableIf() semantics inverted and re-timed. The closure now evaluates per render rather than once at registration, and a closure returning true now disables — previously the result was applied inverted and could disable the package globally. Callers who compensated for the old inverted behaviour will find their conditionals now doing the opposite of what they intended. This is the highest-risk change because the failure is logical, not a fatal error.description() to the tables::header.after hook so existing descriptions survive. Good for correctness, but it introduces a documented blind spot: the header container is hidden by Filament when a table has no heading, description, header actions, search, filters or column manager, so links silently do not render on bare tables. Silent absence is worse than an error for support burden.Key questions:
build(), buildResourceLinks() or buildFileLinks() that treat the return value as a string?disableIf() anywhere, and were any workarounds applied for the old inverted behaviour?Stack fit: Assumes Laravel with Filament panels. The package adds no database migrations, no config file requirements, and no queue or cache dependencies, so it composes with essentially any Laravel stack. The only stack constraint introduced in v2 is PHP >= 8.2 and a Filament tables version in the 3–5 range. Composer will enforce both, which is the desirable failure mode: resolution fails loudly rather than degrading at runtime.
Migration path: Straightforward but not automatic. Sequence:
disableIf() calls, any code consuming build()/buildResourceLinks()/buildFileLinks() output.['url' => ..., 'label' => ...] arrays rather than HTML strings; if custom markup is needed, either publish the Blade view or iterate the arrays in our own view.disableIf() closure's polarity and confirm the intent still matches now that true means disabled and evaluation is per-render. Do not port old workarounds.UPGRADING.md for any steps not enumerated here.Compatibility: Broad on Filament (v3 through v5 in one release line, backed by a CI matrix over PHP 8.2/8.3/8.4 × Filament v3/v4/v5 — unusually disciplined for a package this size). Narrow on PHP: 8.2 minimum. The filament/tables constraint is now declared rather than implicitly assumed, which removes a prior class of "works on my machine" resolution surprises where a project happened to have tables installed transitively.
Sequencing: Land the PHP 8.2 floor first as a platform prerequisite, independently of this package, so the upgrade is not entangled with unrelated runtime changes. Then upgrade the package with the consumer rewrites (step 2) and disableIf() audit (step 3) in the same change set — they are contract-coupled and splitting them leaves the codebase in a state where the types and the conditionals disagree. The bare-table audit (step 5) can follow as a visual QA pass on each affected resource. Given the silent-failure characteristics, do a full visual sweep of panel tables rather than relying on tests alone.
Maintenance: Low ongoing cost. Tiny surface area, one maintainer-plus-contributor cadence, SemVer-honest major version with a written UPGRADING.md. The addition of a Pest suite and a three-by-three CI matrix in this release is the strongest signal here — it means future Filament majors are likely to be caught in CI rather than by us in production. Residual risk is bus factor: a single primary maintainer on a package that is pure convenience. Mitigate by keeping the published Blade view as the customisation seam rather than forking the package, so an abandonment scenario costs us a view file and a plugin registration line, not a rewrite.
Support: Support load should be near zero after the migration, with two exceptions to brief the team on. First, the disableIf() polarity flip is a trap for anyone reading old code or old documentation — expect at least one "links are missing" or "links won't go away" ticket traceable to a stale inverted closure. Second, the bare-table limitation is a silent no-render, and the natural first instinct is to file it as a bug rather than consult the known-limitations note. Document both in the internal runbook at rollout, not after the first incident.
Scaling: Non-issue. Rendering is per-request Blade output with a handful of links; there is no query cost, no N+1 risk, no cache interaction. The only scaling-adjacent consideration is that disableIf() now evaluates per render instead of once at registration — if any closure does meaningful work (permission checks against the database, external calls), that cost is now paid on every table render rather than once. Keep those closures cheap and memoised.
Failure modes:
disableIf() logic: The dangerous one. Produces wrong-but-plausible behaviour, and could leave links exposed to users who should not see them, or hidden from those who should. Requires deliberate review, not testing alone.build() output either fatal-errors (if typed) or stringifies to something unhelpful. Caught quickly in QA if any test touches it.Ramp-up: Minimal. A developer familiar with Filament can read the source in one sitting; the public API is a handful of builder methods plus a Blade view. Budget roughly half a day for the first integrating engineer, most of which is the disableIf() reasoning and the bare-table check rather than the API itself. The migration from v1 is a similar magnitude, dominated by the consumer rewrites and the polarity audit. No specialist knowledge, no training material, no dedicated owner required.
How can I help you explore Laravel packages today?