Jan 25, 2022 · 3 minutes read

Sending flash notifications with Laravel and Inertia.js

While coding on security policies on a little project I've encountered the following issue: After denying a specific action for a user, I would like to show them in a nice and clean way, why they can't perform that given action. And except for that, (flash) notifications are a really nice way to...

While coding on security policies on a little project I've encountered the following issue: After denying a specific action for a user, I would like to show them in a nice and clean way, why they can't perform that given action. And except for that, (flash) notifications are a really nice way to inform your user about other stuff, like a successful model creation, etc.

Backend

To make sure the logged-in user can perform an action, I check that with policies in my controller.

1$this->authorize('view', $post);

If the user is unauthenticated or doesn't have the rights to perform the request it will throw a 403 error. Since I would like to override the behaviour of displaying such an error, I've modified the render method in my App\Exceptions\Handler.php file slightly:

1public function render($request, Throwable $e)
2{
3 $response = parent::render($request, $e);
4 
5 if($response->status() == 403) {
6 return redirect()->back()->with('notification', [
7 'color' => 'red',
8 'title' => 'Error',
9 'message'=> $e->getMessage(),
10 ]);
11 }
12 
13 return $response;
14}

When the error code is 403, it will redirect my user back to the last visited page, including flash data for my notification. Since I would like to use these notifications for other events too, it makes sense to pass a color or style variable here as well.

Communication to Frontend

To pass the created flash data to the frontend, I've modified my App\Http\Middelware\HandleInertiaRequests.php middleware. I've added a share function to it, which passes the data to my $page.props object.

1public function share(Request $request)
2{
3 return array_merge(parent::share($request), [
4 'flash' => [
5 'notification' => fn () => $request->session()->get('notification')
6 ],
7 ]);
8}

Frontend

To display that just created notification, I've created a Vue component called NotificationBox. It contains the template part:

1<template>
2 <div
3 class="fixed inset-0 z-50 flex items-end justify-center px-4 py-6 pointer-events-none sm:p-6 sm:items-start sm:justify-end">
4 <transition
5 appear
6 enter-active-class="transform ease-out duration-300 transition"
7 enter-class="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2"
8 enter-to-class="translate-y-0 opacity-100 sm:translate-x-0"
9 leave-active-class="transition ease-in duration-100"
10 leave-class="opacity-100"
11 leave-to-class="opacity-0">
12 <div
13 v-show="notification && show"
14 class="max-w-sm w-full bg-white shadow-lg rounded-lg pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden"
15 @mouseleave="show = false">
16 <div class="p-4">
17 <div class="flex items-start">
18 <div class="flex-shrink-0">
19 <Icon :class="'text-' + notification.color + '-400'" :icon="icon"/>
20 </div>
21 <div class="ml-3 w-0 flex-1 pt-0.5">
22 <template v-if="notification.title && notification.message">
23 <p class="text-sm font-medium text-gray-900">
24 {{ notification.title }}
25 </p>
26 <p class="mt-1 text-sm text-gray-500">
27 {{ notification.message }}
28 </p>
29 </template>
30 <template v-else>
31 <p class="text-sm font-medium text-gray-900">
32 {{ notification.message || notification.title }}
33 </p>
34 </template>
35 </div>
36 <div class="ml-4 flex-shrink-0 flex">
37 <button
38 class="bg-white rounded-md inline-flex text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
39 @click="show = false">
40 <span class="sr-only">Close</span>
41 <icon icon="x" class="h-5 w-5"/>
42 </button>
43 </div>
44 </div>
45 </div>
46 </div>
47 </transition>
48 </div>
49</template>

Which uses a beautiful notification template from TailwindUI, as well as the logic to display a notification in the correct color, with the correct icon and with or without title. It also contains the Javascript part, which isn't that much:

1<script>
2import Icon from "@/Shared/Icon";
3 
4export default {
5 components: {Icon},
6 data() {
7 return {
8 show: true,
9 }
10 },
11 
12 computed: {
13 notification() {
14 return this.$page.props.flash.notification;
15 },
16 
17 icon() {
18 return {
19 red: 'exclamation-circle',
20 green: 'check-circle',
21 }[this.notification.color];
22 }
23 },
24}
25</script>

I would like that my users can hide the notification and because of that I've created a show property, which will hide the notification when set to false. For simplicity it also contains the notification as computed property, so I don't have to write this.$page.props.flash.notification too often.

In closing

Flash notification are a very nice way to present information to your user, which should not override the full page. The given code creates an output like the following:

The user can hide the notification, and it will get automatically hidden after the users cursor leaves the notification.

Did you enjoy what you’ve read?

Once in a while I send an email with some project updates, article drafts or other cool stuff. I won’t share your email and I won’t let your inbox overflow. Promised.