arcanedev/notify
Arcanedev Notify is a lightweight Laravel package for managing and displaying flash notifications. Queue success, info, warning, and error messages in the session, then render them in your views via a simple helper, Blade directive, or facade, keeping notification logic clean and reusable.
Install and let Laravel discover it
composer require arcanedev/notify
On Laravel 5.5+ the service provider and the Notify facade alias are auto-discovered. On older skeletons (or if you've disabled discovery) add to config/app.php:
'providers' => [
Arcanedev\Notify\NotifyServiceProvider::class,
],
'aliases' => [
'Notify' => Arcanedev\Notify\NotifyFacade::class,
],
Publish and read the config before writing any code
php artisan vendor:publish --provider="Arcanedev\Notify\NotifyServiceProvider"
Then open config/notify.php. This is the single most important step — the package is small, under-documented, and the config file is the real API surface. Look for the view name, the session key, and the list of supported types. Everything else in this assessment assumes you've confirmed those keys against the installed version.
Wire the render call into your layout once
Pick a single layout (resources/views/layouts/app.blade.php) and add the render call where alerts should appear — typically directly above @yield('content'):
<body>
@include('layouts.partials.nav')
<div class="container">
{!! notify()->render() !!}
@yield('content')
</div>
</body>
If notify() is not registered as a helper in your version, use the facade instead: {!! Notify::render() !!}.
First use case: flash after a redirect
// app/Http/Controllers/PostController.php
public function store(StorePostRequest $request)
{
$post = Post::create($request->validated());
notify()->success("Post \"{$post->title}\" was created.");
return redirect()->route('posts.index');
}
Load posts.index, submit the form, and you should see the alert exactly once. That's the whole loop — the package is a thin, opinionated wrapper around session()->flash() plus a renderer.
Post/Redirect/Get is the contract
notify()->success(...) writes to the session for the next request. It is not a return value and it will not render in the current response. Every call site should sit immediately before a redirect() or back():
public function update(UpdatePostRequest $request, Post $post)
{
$post->update($request->validated());
notify()->success('Changes saved.');
return back();
}
If you find yourself wanting an inline alert on the current page, don't fight the package — pass a view variable instead.
Wrap it in a tiny application service
The facade alias Notify is generic enough to collide, and you'll want a seam for testing. A one-class wrapper pays for itself immediately:
namespace App\Support;
use Arcanedev\Notify\Notifier;
class Flash
{
public function __construct(private Notifier $notifier) {}
public function success(string $message, ?string $title = null): void
{
$this->notifier->success($message, $title);
}
public function error(string $message, ?string $title = null): void
{
$this->notifier->error($message, $title);
}
public function fromException(\Throwable $e): void
{
$this->error(config('app.debug') ? $e->getMessage() : 'Something went wrong.');
}
}
Inject Flash into controllers, jobs, and listeners. If a future Laravel version breaks the package, you swap one class instead of grepping the codebase.
Centralize error flashing in the exception handler
The highest-leverage integration is app/Exceptions/Handler.php — flash once, render everywhere, and stop repeating yourself in every controller:
public function register()
{
$this->renderable(function (ValidationException $e, $request) {
if ($request->expectsJson()) {
return null;
}
notify()->error('Please correct the highlighted fields.');
return back()->withInput()->withErrors($e->errors());
});
}
This is where a flash library earns its keep: one line covers every form in the app.
Group multiple messages in one request
Batch operations should report an aggregate, not fire fifty alerts:
$results = $this->importer->run($file);
notify()->success("{$results->imported} rows imported.");
if ($results->failed > 0) {
notify()->warning("{$results->failed} rows were skipped — see the log.");
}
Whether the renderer stacks or overwrites multiple messages of the same type depends on the package's session structure. Check session()->get(config('notify.session_key')) after a request to see the shape before you design around it.
Override the published view rather than the vendor one
Publish the views (--tag=views, or check php artisan vendor:publish for the exact tag), copy the package's Blade file into resources/views/vendor/notify/, and point config/notify.php at a view you control. That's where you add Bootstrap/Tailwind classes, icons, dismiss buttons, and role="alert" accessibility attributes. Never edit vendor/.
Bridge to a JS toast library
If you use Toastr/SweetAlert/Notyf, you don't need the Blade view at all. Render the payload into a data attribute and let your JS pick it up:
<div id="flash"
data-flash="{{ json_encode(session(config('notify.session_key'), [])) }}"></div>
const el = document.getElementById('flash');
const messages = JSON.parse(el.dataset.flash || '[]');
Object.values(messages).flat().forEach(m => window.toastr.info(m));
This keeps the server-side flash semantics and gives you full control over presentation.
Test the flash, not the markup
Feature tests should assert the session, not scrape HTML:
public function test_post_creation_flashes_success()
{
$this->actingAs($user)
->post(route('posts.store'), ['title' => 'Hello'])
->assertRedirect(route('posts.index'))
->assertSessionHas(config('notify.session_key'));
}
For the render path, follow the redirect ($this->followRedirects()) and assert on your own Blade partial's output — that's the part you actually own.
Where it fits in a middleware stack
Useful spots beyond controllers: an Authenticate override to flash "Please sign in to continue", a subscription middleware to flash "Your trial has ended", and a listener on Illuminate\Auth\Events\Verified to confirm email verification. All of these are the same one-liner, which is the point of the package.
The package is effectively unmaintained — pin it deliberately. Last release was December 2020. Its illuminate/* constraints likely stop at Laravel 6/7/8. Before adopting, run composer require arcanedev/notify -W and confirm it resolves against your Laravel version; if not, this assessment becomes a 20-minute exercise in writing a 40-line Flash class yourself (session put + a Blade partial). The API surface is small enough that "vendor it" is a legitimate strategy.
Flash data dies after exactly one request. Classic failure: you flash a message, the redirect target itself redirects again (auth middleware, locale middleware, trailing-slash normalization), and the alert appears on the wrong page — or vanishes. If messages disappear mysteriously, dump session()->all() at the top of the layout to see whether the key is still present. Fixes are session()->reflash() in the intermediate middleware, or moving the flash to the final destination.
Rendering happens in the layout, so a missing render() call is invisible. No exception, no log entry — just silence. When a developer says "notifications aren't working", 90% of the time the layout they're rendering doesn't include the call. Add it to layouts/app.blade.php and any auth/error layouts, which are easy to forget.
Watch the escaping on both sides. Your layout almost certainly uses {!! ... !!} because the renderer returns HTML. That means the package must escape the message strings internally — verify this before you ever pass user-supplied text (a post title, an email address) into a notification. If it does not escape, wrap your interpolations yourself with e() or Str::limit(e($title), 60). This is the one place where a stale package becomes a genuine XSS liability.
API/JSON consumers are out of scope. These are session flashes. For expectsJson() requests, return the message in the JSON body instead of calling notify() — otherwise the controller writes to a session nobody reads, and the frontend shows nothing. A quick guard in your Flash wrapper (if (request()->expectsJson()) { ... }) handles this.
Test suites with the array session driver still flash correctly, but nothing persists across requests. assertSessionHas() works because it inspects the redirect response's session; get() on the next request may not. Don't write a test that flashes in one call and asserts a rendered alert in a separate un-redirected request.
Config caching bites after publishing. Run php artisan config:cache (and config:clear in local). If you change the view name in config/notify.php and see no difference, you're looking at a cached config.
Facade alias collisions. Notify is a very common name — Laravel Spark, various notification packages, and plenty of app-level facades use it. If you get "class not found" or, worse, silently wrong behavior, drop the alias from config/app.php and use the fully-qualified class or your Flash wrapper.
Extension points are limited, and that's fine. The realistic customization surface is: the published config (types, view, session key), the published view, and the wrapper class. Don't expect macros, channels, or queued notifications — that's what laravel/framework's notification system and packages like spatie/laravel-flash are for. Choose this package for the 5-minute setup, not the ecosystem.
Reasonable alternatives if you're starting fresh. Laravel's built-in session()->flash('status', ...) plus ten lines of Blade covers 80% of this. spatie/laravel-flash and laracasts/flash are actively maintained and do the same job with better docs. Adopt arcanedev/notify because it's already in the project — not because it's the best tool in the category.
How can I help you explore Laravel packages today?