pushery/visual-feedback-for-laravel
Visual feedback for Laravel apps: surface toasts, alerts, and inline UI cues so users always know what happened. Simple installation, sensible defaults, and Blade-friendly helpers keep feedback consistent without extra frontend work.
The first thing to resolve is that the package is not what the listing says it is. The blurb sells a fluent helper API that renders toasts, notifications and progress indicators — a replacement for flash messages. The shipped code is an in-page feedback widget: a floating trigger, a modal report form, client-side screenshot capture with optional annotation, abuse protection, and pluggable delivery (mail / database / webhook). There is no success()->toast() surface to adopt. Any roadmap item scoped as "replace our flash-message layer" must be pulled or re-scoped before estimation; evaluating this package against the description rather than against config/visual-feedback.php is the largest single planning error available here.
Architecture fit. It is a conventional Laravel package: auto-discovered service provider, Blade views, config, translations, and a Livewire component for the widget itself. It mounts into any layout with three includes. Fit is high for plain Blade and Livewire apps, which is most of our estate; the Inertia/Vue surfaces are a question mark, since a Livewire component embedded in a client-rendered shell needs per-route verification rather than a one-line acceptance.
Integration feasibility. Genuinely low engineering effort — no build step, no npm, no compiler to add to CI. Four JS bundles publish to public/vendor/visual-feedback. The honest framing for planning is: no build step, but a deploy step. The bundles are copies, not symlinks, so they go stale on every composer update, and a stale bundle renders a widget whose controls silently do nothing. php artisan about reports bundle freshness — that single command should be a deploy gate.
Technical risk. Ranked by what would actually hurt:
| Risk | Severity | Mitigation owner |
|---|---|---|
| Description/artifact mismatch drives wrong scoping | High | TPM — re-baseline the requirement before approval |
| Maturity: 6 stars, 0 dependents, single maintainer, pre-1.0 (v0.20.x), MIT | High | Engineer — confine to one partial, pin version |
Silent delivery failure (no worker, no recipient, MAIL_MAILER=log, stale bundles) — none throw |
High | Engineer — instrumentation + alert in first PR |
| Screenshots capture whatever was on screen; no built-in redaction | High | TPM — classify data, set retention, own DSAR path |
Livewire coupling; wire:navigate bundle-ordering history |
Medium | Engineer — test first navigation specifically |
| CSP blocks in production while local works | Medium | Engineer — policy edit ships with layout edit |
Key questions before approving: Which surface accepts reports first? Who triages, and in what tool? What is the data classification of a screenshot of that surface? Which channel — mail, webhook, or database? Is Livewire already a dependency, or does this introduce it? Who owns retention, DSAR response and redaction policy? What does success look like at day 30?
The changelog is the mitigating signal: 0.18.1 and 0.20.1 are real production-edge fixes (PostgreSQL type errors, pagination stability, abuse-path upload billing, wire:navigate ordering). That is a maintainer actively hardening, not abandoning — a reason to pin and upgrade deliberately rather than to reject.
Stack fit. Plain Blade and Livewire are first-class; Inertia and API-driven front ends should be validated per route before commitment. The package introduces a Livewire rendering path onto whichever page carries the widget — if that page is not already Livewire, treat it as a new runtime dependency, not an include.
Migration path is additive, not a swap. Nothing needs migrating. What this replaces is the informal channel: a mailto:support@ link, a "something broke?" footer link, or a third-party widget whose data leaves our boundary. Frame the change as consolidating intake, which is how the business case gets funded — fewer re-filed reports, faster reproduction, reports that arrive already in triage.
Compatibility checks to run in the first hour: Laravel and PHP version constraints against our LTS baseline, Livewire major version, and database engine if the database channel is ever enabled (MySQL, SQLite and PostgreSQL are all handled as of 0.18.1, including malformed-ID and bad-date bounds that previously errored on Postgres).
Sequencing — phase-gated, smallest viable loop first:
composer.json; no floating constraint on a pre-1.0 package.partials/feedback.blade.php behind a config flag. This is the structural risk buy-down: removal, environment opt-out and forking all collapse to one-line operations.VISUAL_FEEDBACK_MAIL_TO set, MAIL_MAILER verified as a real transport, and a queue worker running. Screenshot strategy dom — deterministic, no permission prompt, smallest operational tail.script-src.database is a genuine commitment — it adds a migration (published outside the umbrella tag, deliberately), a retention prune, and a triage UI to own — adopt it only if we want a system of record.Maintenance. Two recurring costs, both small and both forgettable: re-publishing assets after every composer update, and reading the changelog before each upgrade. Releases are actively fixing production edges, which is a good sign and a reason not to float. Budget roughly one engineer-day per quarter for upgrades and verification.
Support and bus factor. Single maintainer, zero dependents, no vendor SLA. The realistic contingency is a fork — MIT licensed, small enough to own — or replacement. That contingency should be written down, not assumed; it is what makes the maturity risk acceptable rather than reckless.
Scaling. Front-end weight is modest and well-behaved: the ~276 KB renderer is lazy-fetched only when a screenshot is actually taken, and screenshot.strategy=off drops it entirely for typed-only reports. Server-side, every channel dispatches a job, so scaling is a queue-capacity question and reports compete with existing workload. Abuse is bounded by per-sender rate limits (defaults 30 authenticated / 5 guest) and, since 0.18.1, refusals occur before storage — over-limit guests and spent rate limits no longer copy files to the attachments disk and delete them, which on S3 removed a billed PUT+DELETE per file per retry. Screenshot retention belongs on the private attachments disk and needs a scheduled prune driven by retention_days.
Failure modes — the dominant risk class is silent refusal. No worker, unset recipient, MAIL_MAILER=log, or stale bundles all accept the report and drop it quietly, with the reporter told it failed and nothing alerted. Design against this rather than debugging it later. One trap worth naming to the team: rate limit 0 is not an off switch — since 0.18.1 those values floor to 1 and hold at the shipped 30/5 in the config array too, so inherited code reading visual-feedback.abuse.rate_limit directly now sees 30/5 where it saw 0. Only the instance-wide global_rate_limit reads 0 as off. Never leave MAIL_MAILER=log on a staging box that accepts feedback — a base64 screenshot in the log stack propagates into every error tracker behind it.
Ramp-up. Engineering: half a day to wire, test and instrument. TPM/policy: the longer lead time, and the one most likely to be skipped — data classification for screenshots, retention scheduling, DSAR response path, and confirming backups inherit the same classification. There is no built-in redaction; that is ours.
How can I help you explore Laravel packages today?