genealabs/bones-flash
GeneaLabs Bones Flash is a Laravel package for session-based flash notifications. It provides a clean API to queue success, error, warning, and info messages and render them in your views, making consistent user feedback easy across your application.
Confirm what you're actually installing. The package name (genealabs/bones-flash) doesn't match its repository (GeneaLabs/laravel-messenger), so don't paste a composer require blindly. Check the metadata first in a scratch app:
composer show genealabs/bones-flash --all
Read the requires block before anything else. The last release was March 2020, which places this in the Laravel 5.5–7 / pre-PHP 8 era. On Laravel 9+, your gate is:
composer why-not genealabs/bones-flash
If it only resolves by pinning the framework down, stop — read the source as a pattern and write thirty lines yourself instead.
Install and register.
composer require genealabs/bones-flash
On the framework versions this targets, package auto-discovery should register GeneaLabs\LaravelMessenger\Providers\ServiceProvider. On older or manually-curated setups, add the provider (and any facade alias it declares) to config/app.php.
Orient yourself in about sixty seconds. The package is small enough to read in one sitting, and you should — it's the fastest way to learn the exact API surface:
ls vendor/genealabs/bones-flash/src
ls vendor/genealabs/bones-flash/resources/views
grep -rn "loadViewsFrom\|loadTranslationsFrom\|publishes" vendor/genealabs/bones-flash/src
That gives you the three things you need before writing code: the facade/helper name, the Blade namespace, and the vendor:publish tags.
Publish the views so you own presentation rather than fighting it later:
php artisan vendor:publish --provider="GeneaLabs\LaravelMessenger\Providers\ServiceProvider"
Wire the partial into your layout once, inside <body> and above @yield('content'):
@include('messenger::messages')
Verify the view name against loadViewsFrom — the documented include is messenger::messages, but confirm rather than assume.
First real use case — the classic post-redirect-get:
public function store(StorePostRequest $request)
{
$post = Post::create($request->validated());
messenger()->success('Post saved.');
return redirect()->route('posts.show', $post);
}
Sanity-check by hitting the route and confirming the banner renders after the redirect and is gone on the next request. Renders twice? Your layout includes the partial more than once. Never renders? Check the session driver and that the partial sits in the layout the redirect target actually uses.
Keep it at the edges. Call the helper/facade from controllers and form requests only — never from models, jobs, or queued listeners, where the request/session context either doesn't exist or flashes into the wrong one. The natural pairing is a FormRequest for validation and one flash call after the write completes.
Stack messages within a single request. Messages queue per session rather than overwriting, so one action can report several outcomes:
messenger()->success('Import finished.');
messenger()->warning("{$skipped} rows were skipped.");
return back();
The partial iterates the queue, so both banners render on the next load. Resist five separate warnings — the UI degrades fast. Aggregate.
Use levels semantically. success = the write happened. error = the write didn't happen and the user must act. warning = partial success. info = nothing changed, but you should know. Do not push field-level validation failures into flashes — those belong in $errors with your existing @error markup. Duplicating them into banners trains users to ignore both.
Wrap it behind a thin app-level seam. This is the single highest-value day-to-day pattern with this package:
// app/Support/Flash.php
class Flash
{
public static function success(string $m): void { messenger()->success($m); }
public static function error(string $m): void { messenger()->error($m); }
}
Every call site now depends on your class. When you drop the package — and on modern Laravel you probably will — you rewrite ~15 lines to session()->flash('flash.success', $m) and swap in a Blade component. That's an afternoon's work instead of a refactor.
Own the markup. Once published to resources/views/vendor/messenger/, replace the package's classes with your design system's alert classes. That's also where you add dismiss buttons, icons, and ARIA roles (role="alert" for errors, role="status" otherwise). Keep the loop structure identical to the vendor original so the diff stays auditable.
Bridge to JS if you already run toasts. With Alpine, Livewire, or Vue in place, skip the package's rendering and hand the session data to your toast library:
<script>
window.__flash = @json(session()->get('messenger', []));
</script>
Test it like any other session behavior:
$response = $this->post('/posts', $data);
$response->assertSessionHas('messenger'); // confirm the real key from the provider
$this->followRedirects($response)->assertSee('Post saved.');
Also add a regression test asserting the flash does not survive a second request — that's the invariant most likely to break silently after a framework bump.
Maintenance risk is the headline gotcha. Five-plus years unmaintained, zero dependents, no security disclosure process. Your day-to-day cost isn't integration — it's that you become the maintainer of every bug you hit. Budget for that explicitly and keep the footprint to a handful of call sites behind your own facade.
Escaping becomes your responsibility once you publish views. Flash payloads routinely carry user-supplied text — a name, a filename, an imported record title. Use {{ }} everywhere in the published Blade view. Audit the vendor original for {!! !!} before trusting it, and add a test that flashes <script>alert(1)</script> and asserts it renders escaped. This is the one failure mode here that's a security incident rather than an annoyance.
The session cookie driver has a ~4KB ceiling. Flashing a long validation summary or a list of failed rows will truncate or blow the cookie, and the symptom is a mysteriously empty session or a 500 — not an obvious flash bug. Cap message length at the call site and never interpolate arbitrary user input wholesale.
Auto-discovery dies quietly on framework upgrades. Symptom: Class "GeneaLabs\..." not found at boot, or the partial renders nothing because the view namespace never registered. When that happens, register the provider explicitly in bootstrap/providers.php / config/app.php, then run php artisan view:clear — stale compiled views are a frequent red herring after publishing.
Debug the session, not the package. When a message doesn't appear, dd(session()->all()) in the layout just before the include. You'll immediately see whether the write never happened (call site in a job/queue/middleware without session), happened into the wrong session (driver or domain mismatch), or happened and rendered nowhere (partial missing, or @include in a branch that isn't hit).
Watch the flash lifecycle around back() and redirect()->intended(). Both are fine, but flashes written before a middleware that regenerates the session — or in a terminated/queued context — vanish. Write flashes as the last thing before returning the redirect.
Don't fork the provider. If you need behavior it doesn't offer, publish the views (the intended extension point) and wrap the API in your own helper. Forking buys you a merge conflict on every framework bump and, given the release history, there won't be bumps worth merging. If you truly must own the code, copy the ~150 lines into app/Support/ under your namespace and delete the dependency — cheaper than a fork.
Name/repo drift means patched source may not be what's running. composer show --path the package before patching anything, and if you do need a fix, use a Composer vcs repository override or a fork with an explicit version constraint so CI on other machines installs identical code.
Plan the exit on day one. Add a short ADR: why the module exists, where the wrapper lives, who owns it, and the ticket to replace it with session()->flash() plus a Blade component. A package nobody is upstreaming fixes into becomes orphaned code the next team is afraid to touch — a two-line note now prevents that.
How can I help you explore Laravel packages today?