With Inertia on Laravel, how do you show a one-time 'report exported' toast with Inertia::flash, and why not share it as a regular prop?
answer
- one-time data, not a prop
- Inertia::flash(key, value) then back()
- page.flash, outside props
- onFlash callback and flash event
- props live in history state
basics
~20 sCall Inertia::flash('toast', [...]) and return back() (or chain ->back()); the next page object carries it under flash, outside props. Because flash is not stored in history state, the toast does not reappear when the user presses Back.
solid answer
~40 sIn the action, call `Inertia::flash('toast', ['message' => 'Report exported'])` and return `back()`, or write `return Inertia::flash('toast', $toast)->back();`. `flash()` also takes an array of pairs, and it can be chained onto `Inertia::render()` when no redirect is involved. The adapter keeps the data in the session until the next Inertia response, puts it in the page object's top-level `flash` key and then clears it. On the client, read it from `usePage().flash`, handle it once with the `onFlash` visit callback, or listen globally with `router.on('flash', ...)`. The reason not to share session flash through `share()` is history: props are saved in the browser's history state, so a toast shared as a prop comes back when the user presses Back. Inertia's flash data is stripped from history state and is delivered once.
code
php · 21 lines<?php
namespace App\Http\Controllers;
use App\Jobs\ExportReport;
use App\Models\Report;
use Illuminate\Http\RedirectResponse;
use Inertia\Inertia;
class ReportExportController extends Controller
{
public function store(Report $report): RedirectResponse
{
ExportReport::dispatch($report, request()->user());
return Inertia::flash('toast', [
'type' => 'success',
'message' => 'Report export started',
])->back();
}
}go deeper
Recall Inertia::flash() with back() on the server and usePage().flash in the layout for one-time toasts.
Explain that flash data is a top-level page key pulled from the session once, and why props-based toasts reappear on Back.
Choose between onFlash, the global event and rendering from the page, and test flashes with assertInertiaFlash.
Standardise a notification contract, such as keys, types and localisation, so every team's toasts behave the same.
## What flash data is for Toasts, "saved" banners and a freshly created record's id are **one-time** values: shown after an action, never again. Inertia's Laravel adapter has a dedicated channel for them, `Inertia::flash()`, separate from props. ## Server side The API is small: - `Inertia::flash('toast', ['type' => 'success', 'message' => 'Report exported'])` flashes one key. - `Inertia::flash(['toast' => $toast, 'reportId' => $report->id])` flashes several at once. - `Inertia::flash('toast', $toast)->back()` flashes and redirects in one expression. - `Inertia::render('Reports/Index', $props)->flash('highlight', $report->id)` flashes without a redirect. - Keys may also be backed or pure enum cases. Under the hood the adapter stores the values in the session's flash data under its own key. When the next Inertia response is built, it **pulls** them from the session, so they are removed once delivered, and adds a top-level `flash` object to the page object. The middleware keeps flash data alive across redirects and across an asset-version reload, so it survives the extra hop. ## Client side There are three ways to consume it: 1. **Render from the page object.** In React, `const { flash } = usePage()`, then show `flash.toast` if present. The layout is the natural place. 2. **Per visit.** `router.post(url, data, { onFlash: (flash) => ... })`, useful for reading a new id right after a submission. 3. **Globally.** `router.on('flash', (event) => showToast(event.detail.flash.toast))`, registered once in a layout. Remove the listener when the component unmounts. ## Why not share it as a prop The older pattern shared Laravel's session flash through the middleware: ```php 'flash' => ['success' => fn () => $request->session()->get('success')], ``` It works, but it has two drawbacks: | Aspect | Session flash shared as a prop | `Inertia::flash()` | |---|---|---| | Where it lives | inside `props` | top-level `flash` key | | History state | saved with the page, so Back shows the toast again | stripped from persisted history state | | Events | none; components diff props themselves | `flash` event and `onFlash` callback | | Payload | the key is on every response, usually `null` | present only when there is something to show | The history problem is the one users notice: they export a report, navigate away, press Back, and the "Report exported" toast appears again because the whole props object was restored. ## Testing The adapter adds `assertInertiaFlash('toast')` and `assertInertiaFlashMissing('toast')` to `TestResponse`, which check the flashed session data after a redirect, and `hasFlash()` and `missingFlash()` on the `assertInertia` object for a rendered page. ## A worked flow For the analytics dashboard's export button: 1. The page posts to `/reports/{report}/export` with `router.post()` or a form component. 2. The action dispatches the export job, calls `Inertia::flash('toast', [...])` and returns `->back()`. 3. The browser follows the redirect inside the visit; the `GET` of the dashboard builds a page object whose `flash` key holds the toast, and the session copy is removed. 4. The layout's `flash` listener shows the toast once. Navigating away and pressing Back restores the dashboard's props from history, without the toast. 5. A refresh of the dashboard sends no flash either, because the session copy was already pulled. If the same action is also reachable outside Inertia, the flash still sits in the session and is delivered with the next Inertia page response. ## Practical rules - Flash the smallest thing the UI needs: a type, a message key, maybe an id. - Translate messages on the server if the app is localized, or send a key the client maps. - Never flash sensitive data; it still reaches the browser. - Keep validation errors out of flash data; they travel in the `errors` prop.
- How do you read a newly created record's id right after a form post?Flash it on the server, for example `Inertia::flash('reportId', $report->id)->back()`, and read it in the visit's `onFlash` callback: `router.post(url, data, { onFlash: ({ reportId }) => ... })`. The callback runs once for that visit, so the id does not linger in props or history.
- Does Inertia flash data survive a redirect chain?Yes. It is stored in the session and pulled only when an Inertia page response is built, and the middleware reflashes it when a response is a redirect. It also re-flashes session data when an asset-version mismatch forces a reload, so the toast is not lost on that extra hop.
saying these in an interview costs you the question
- Inertia flash data is available as page.props.flash.
- Flash data is kept in history so Back can show it again.
- back()->with('toast', ...) fills Inertia's flash key by itself.
- Inertia::flash only works when followed by a redirect.
- Flash data stays on every later page until the session expires.