A flash message is data stored in the session for exactly one subsequent request — the classic "your changes have been saved" banner that shows once after a redirect, then disappears on the next page load.
Setting a flash message
public function store(Request $request)
{
Order::create($request->validated());
return redirect()->route('orders.index')->with('success', 'Order created successfully.');
}
->with() on a redirect response is shorthand for flashing that key to the session — it only persists for the next request, then it's gone.
Displaying it in a Blade layout
@if (session('success'))
<div class="alert alert-success">{{ session('success') }}</div>
@endif
@if (session('error'))
<div class="alert alert-danger">{{ session('error') }}</div>
@endif
Putting this check in a shared layout (rather than every individual view) means any controller can flash success or error and have it show up automatically after the next redirect, without each view needing its own display logic.
Why it "disappears" — and why that's the point
Flash data is automatically removed from the session after being read on the very next request — this is the intended behavior, not a bug. If you need a message to survive across more than one request, session()->flash() is the wrong tool; store it as regular (non-flash) session data and clear it yourself when appropriate instead.
Multiple message types at once
return back()->withErrors(['email' => 'That email is already taken.']);
// vs
return back()->with('error', 'Something went wrong.');
withErrors() is specifically for validation-style field errors (available via $errors in Blade) — a general one-off flash message like "Something went wrong" is a different, simpler mechanism and shouldn't be mixed into the validation error bag.
Flash data is available for the next request, not necessarily only after it is read
The useful mental model is request lifecycle: data is marked to survive into the following request, then Laravel ages it out afterward. You can call session()->reflash() to keep all flash data for another request, or keep(['success']) to retain selected keys when an intermediate redirect would otherwise consume their lifetime.
Use old input for form repopulation, not a custom flash structure
Laravel's validation redirect already flashes input so Blade can repopulate fields with old(). Avoid storing the whole submitted request inside a custom error flash message; that duplicates framework behavior and can accidentally persist sensitive fields. Password inputs in particular should not be repopulated from flashed data.
Escape messages unless the HTML is fully trusted
The example uses Blade's {{ ... }} syntax, which escapes HTML by default. Keep that behavior for messages that could include user-controlled text. Switching to {!! ... !!} because a message needs bold formatting can create an XSS path if any part of the string later comes from request data. If rich markup is genuinely required, render a known component or message code rather than arbitrary HTML stored in the session.
Validation errors can have named bags
Pages with multiple forms can keep their field errors separate instead of every form reading the same default $errors bag. Named error bags are useful when, for example, a login form and a profile form share one page and both have an email field; a generic success/error banner remains independent of those field-level validation details.