RogerManich's avatar

Toast question

Hi,

After I watched the last Laravel course I wanted to try to build something with Laravel + LiveWire. My first goal was expanding the user preferences to accept language and color scheme. It works but in my first design I wanted to use a route + controller to update the user preferences instead of doing it insde the component settings. It worked but I realized that flux::toast doesn't work in this scenario. I've seen a post where explaining that now is supported out of the box. Well, inside the component works fine.

After googling a bit and some IA help I found out that I require to use session to store the message and add some listener in my layout. Now it works but I wonder if I did something wrong and I am reinventing the wheel or the starterkit missing this piece of code.

thank you

a Route example:

Route::post('/lab', function () {
    session()->flash('flux-toast', [
        'text'    => 'Record saved successfully!',
        'variant' => 'success',
    ]);

    return redirect()->back();
})->name('lab')->middleware('auth');

and in my layout after

@persist('toast')
    <flux:toast position="top right" />
@endpersist
@if(session()->has('flux-toast'))
    @php $toast = session('flux-toast'); @endphp
    <script>
        function showToast() {
            Flux.toast({
                text: '{{ $toast['text'] }}',
                variant: '{{ $toast['variant'] ?? 'success' }}',
            });
        }

        if (document.readyState === 'complete') {
            showToast();
        } else {
            document.addEventListener('DOMContentLoaded', showToast, { once: true });
        }
        document.addEventListener('livewire:navigated', showToast, { once: true });
    </script>
@endif
0 likes
5 replies
LaryAI's avatar
Level 58

Certainly! Here’s a clear answer for your question based on current best practices for handling Flux (or Livewire-powered) toast notifications via "classic" controller routes rather than Livewire actions.

TL;DR

You are not reinventing the wheel; your approach using session flash and adding a script block for the toast is correct if you want the toast to persist through a redirect after a POST. Livewire and Laravel Starter Kits do not generally ship with “out of the box” support for triggering JS-based toasts after HTTP (controller) redirects — that’s what Livewire events/components are for. Your solution is idiomatic for controller-to-view scenarios.


Detailed Solution / Best Practice

1. Flashing Message to the Session (Controller/Route)

This is correct:

Route::post('/lab', function () {
    session()->flash('flux-toast', [
        'text'    => 'Record saved successfully!',
        'variant' => 'success',
    ]);

    return redirect()->back();
})->name('lab')->middleware('auth');

2. Showing the Toast (Blade)

Since Blade cannot push a Livewire event directly from a non-Livewire route, the correct way is to check for the session variable and then output a JS script (as you did). Your code is "standard" for such a scenario, and looks like this:

@if(session()->has('flux-toast'))
    @php $toast = session('flux-toast'); @endphp
    <script>
        function showToast() {
            Flux.toast({
                text: @js($toast['text']),
                variant: @js($toast['variant'] ?? 'success'),
            });
        }

        if (document.readyState === 'complete') {
            showToast();
        } else {
            document.addEventListener('DOMContentLoaded', showToast, { once: true });
        }
        document.addEventListener('livewire:navigated', showToast, { once: true });
    </script>
@endif

Improvements:

  • Use @js() (Laravel 10+) for safe JS escaping.
  • Using { once: true } prevents multiple firings.
  • You could also emit a Livewire event if you want, but in this scenario, that would not give you much more.

3. Why isn’t this "out of the box"?

The starter kits (Jetstream, Breeze) and many packages expect Livewire interactions for this kind of thing. They may not have a generic Blade-to-JS toast message (because generally server-side POST + redirect is not expected to fire a JS toast — only in fully client-side/SPA/Livewire interactions or with inertia.js).

4. Alternative

If you want to DRY this up, you can extract this into a Blade component or include for all pages:

resources/views/components/flux-toast-session.blade.php:

@if(session()->has('flux-toast'))
    @php $toast = session('flux-toast'); @endphp
    <script>
        function showToast() {
            Flux.toast({
                text: @js($toast['text']),
                variant: @js($toast['variant'] ?? 'success'),
            });
        }
        if (document.readyState === 'complete') {
            showToast();
        } else {
            document.addEventListener('DOMContentLoaded', showToast, { once: true });
        }
        document.addEventListener('livewire:navigated', showToast, { once: true });
    </script>
@endif

And then just put this near the end of your layout:

<x-flux-toast-session />

Summary

You are doing it right.
Your way is the Laravel-advised approach for classic controller POST/redirect workflows when working with JavaScript toast notifications. There’s no "missing piece" in the starter kits — they just expect Livewire or SPA approaches for this kind of real-time feedback.

If you want, you can make it a reusable Blade component (see above), but otherwise — your solution is both correct and idiomatic!


Happy coding, and welcome to the Laravel + Livewire world!

imranbru's avatar

Hey Roger, you're not reinventing the wheel at all. Since you're doing a standard HTTP redirect instead of a Livewire request, flashing to the session and picking it up with JS on the next page load is exactly the right approach.

I just want to strongly echo LaryAI's note on using @js() instead of {{ }} in your script. Definitely make that switch it automatically JSON-encodes the string and prevents potential XSS vulnerabilities if your toast messages ever end up including unescaped user input down the line.

Extracting your snippet into a simple <x-toast-handler /> blade component and dropping it at the bottom of your main layout is exactly how I handle this in production. Good stuff!

Estinvosh's avatar

One thing I’m curious about: have you considered standardizing it via Laravel’s built-in with() pattern? Something like return back()->with('success', '...') and then mapping that to Flux in one place. Makes it easier to reuse across the app and even swap toast libraries later.

Also wondering did you notice any double-trigger issues with livewire:navigated + DOMContentLoaded, or is { once: true } fully reliable in your case?

RogerManich's avatar

It what I did but it didn't work it. Looks like I need to do something like this:

Route::get('/lab', function () {
       return back()->with('flux-toast', [
        'text' => 'hellou',
        'variant' => 'success',
    ]);   
})->name('lab')->middleware('auth');

Which it is pretty much the same as session()->flash. Maybe there is something easier we can do. But as @imranbru suggest, not bad solution using this approach.

For the moment, with both works fine. Only one is displayed.

Please or to participate in this conversation.