Flash Messages in Laravel Inertia

Video thumbnail

To make use of flash messages in Laravel Inertia, albeit from the controller, we can use the typical operations to set flash-type session messages.

We're going to implement a confirmation message that will be displayed after performing an operation (create, update, or delete).

This works differently than traditional Laravel because Inertia doesn't automatically expose Flash messages to the client. But we can easily configure it.

$request->session()->flash('message', 'Message');

Or by redirection:

with('message','Message');

But, when using components in Vue and not blade views, we have to do an extra step to pass this data via flash session.

Share the flash message with Inertia (HandleInertiaRequests)

In Inertia, we have a middleware that is responsible for intercepting user requests; from this middleware, we can set global data to components in Vue; therefore, if you want to pass global data to components from the database, files, configurations, session, etc:

return array_merge(parent::share($request), [

'appName' => config('app.name'),

'auth.user' => fn () => $request->user()
    ? $request->user()->only('id', 'name', 'email')
    : null,
]);

You must use this middleware; for the case of flash messages, you can use a scheme like the following:

app/Http/Middleware/HandleInertiaRequests.php

 public function share(Request $request): array
    {
        return array_merge(parent::share($request), [
            'flash' => [
                'message' => $request->session()->get('message')
            ]
        ]);
    }

In the example above, we are assuming that we are going to use a single flash message classification, but you can use as many as you need; for example:

app/Http/Middleware/HandleInertiaRequests.php

 public function share(Request $request): array
    {
        return array_merge(parent::share($request), [
            'flash' => [
                'message' => $request->session()->get('message'),
                'status' => $request->session()->get('status')
                 ***
                'other' => $request->session()->get('other')
            ]
        ]);
    }

In the example above, we used an array of arrays to organize the flash messages as it is considered a scalable and organized scheme to be able to define other schemes that you want to pass globally.

Objet $page

The $page object is a variable that can be accessed globally in all components in Vue, it has various data such as:

{
  "component": "Dashboard/Post/Index",
  "props": {
    "jetstream": {
      "canCreateTeams": true,
      "canManageTwoFactorAuthentication": true,
      "canUpdatePassword": true,
      "canUpdateProfileInformation": true,
      "hasEmailVerification": false,
      "flash": [
        
      ],
      "hasAccountDeletionFeatures": true,
      "hasApiFeatures": true,
      "hasTeamFeatures": true,
      "hasTermsAndPrivacyPolicyFeature": true,
      "managesProfilePhotos": true
    },
    "user": {
      "id": 1,
      "name": "andres",
      "email": "admin@gmail.com",
      "email_verified_at": null,
      "two_factor_confirmed_at": null,
      "current_team_id": 1,
      "profile_photo_path": null,
      "created_at": "2022-05-31T15:10:22.000000Z",
      "updated_at": "2022-05-31T15:28:19.000000Z",
      "profile_photo_url": "https://ui-avatars.com/api/?name=a&color=7F9CF5&background=EBF4FF",
      "current_team": {
        "id": 1,
        "user_id": 1,
        "name": "andres's Team",
        "personal_team": true,
        "created_at": "2022-05-31T15:10:22.000000Z",
        "updated_at": "2022-05-31T15:10:22.000000Z"
      },
      "all_teams": [
        {
          "id": 1,
          "user_id": 1,
          "name": "andres's Team",
          "personal_team": true,
          "created_at": "2022-05-31T15:10:22.000000Z",
          "updated_at": "2022-05-31T15:10:22.000000Z"
        }
      ],
      "two_factor_enabled": false
    },
    "errorBags": [
      
    ],
    "errors": {
      
    },
    "data": ...
  "url": "/post",
  "version": "207fd484b7c2ceeff7800b8c8a11b3b6"
}
  • props → The data sent by Laravel
  • url → Current path
  • component → Name of the current Vue component
  • flash → Our flash messages (if configured)
  • auth → Authenticated user information
  • etc.

And of course, any data we set in the HandleInertiaRequests middleware is located here as well.

Display the flash message in the layout

Let's set up confirmation flash messages on each of the redirects defined above:

app/Http/Controllers/Dashboard/CategoryController.php

class CategoryController extends Controller
{

    public function store(Store $request)
    {
        Category::create($request->validated());
        return to_route('category.index')->with('message',"Created category successfully");
    }


    public function update(Put $request, Category $category)
    {
        $category->update($request->validated());
        return redirect()->route('category.index')->with('message',"Updated category successfully");
    }

    public function destroy(Category $category)
    {
        $category->delete();
        return to_route('category.index')->with('message',"Deleted category successfully");
    }
}

To use them from components in Vue, instead of defining them manually in each of the components that we want to have the confirmation message, we can place it in a global component like AppLayout:

resources/js/Layouts/AppLayout.vue

***
<main>
  {{ $page.props.flash.message }}
  <slot />
</main>
***

And when performing any delete, create or update operation in this case, we will have:

Updated category successfully

Style for flash message container

Let's style the above commit message; for that:

resources/js/Layouts/AppLayout.vue

<main>
  <div
   v-if="$page.props.flash.message"
   class="
       container my-2 bg-purple-300 text-purple-800 px-4 py-3 rounded shadow-sm"
      >
        {{ $page.props.flash.message }}
  </div>
</main>

Why does it disappear after the first request?

Because we're using a flash message:

  • ✔ It lasts for one request
  • ✔ It no longer exists after the next request
  • ✔ That's why `$page.props.flash.message` automatically becomes null

This is exactly the same as in traditional Laravel.

Actual operation: Vue + server + Inertia

Although the app looks like a Single-Page Application (SPA), every action you perform (create, update, delete, etc.) triggers a request to the server, and the server:

  • receives the request
  • creates or doesn't create the flash message
  • responds with another Inertia request
  • Vue updates the view with `$page.props.flash`

There's no magic involved:

  • ✔ These are real requests
  • ✔ Flash messages last for only one request
  • ✔ Inertia only helps transport them to the client

Create your first component, a pagination component with Laravel Inertia.

I agree to receive announcements of interest about this Blog.

We will learn how to generate Flash messages in Laravel Inertia, which are messages that only last for one request and are ideal for showing confirmation of an action performed, such as deleting.

| 👤 Andrés Cruz

🇪🇸 En español