Spring Sale 🍃 is live! Get 20% off ANY package with the coupon SPRINGBREAK

Error handling with Inertia.js

One of the nice things about working with a server-side framework is the built-in exception handling you get for free. For example, Laravel ships with Ignition, a beautiful error reporting tool which displays a nicely formatted stack trace in local development.

Laravel comes with a set of very basic error pages. While they serve their main purpose well, the default templates are not much user-friendly and might not fit your website's design.

Thanks to the nature of Inertia.js requests, you can either create custom Blade templates or extend the default exception handler to render error pages as Vue / React components or even throw flash messages on certain exceptions.

The method render of App\Exceptions\Handler is responsible for rendering exception responses. You can define a list of error statuses you want to be displayed as a custom page and return an Inertia response instead of a Blade-served page.

<?php
 
namespace App\Exceptions;
 
class Handler extends ExceptionHandler
{
/**
* A list of error messages
*
* @var array<int, string>
*/
protected $messages = [
500 => 'Something went wrong',
503 => 'Service unavailable',
404 => 'Not found',
403 => 'Not authorized',
];
 
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @return \Symfony\Component\HttpFoundation\Response
*
* @throws \Throwable
*/
public function render($request, Throwable $e)
{
$response = parent::render($request, $e);
 
$status = $response->getStatusCode();
 
if (! array_key_exists($status, $this->messages)) {
return $response;
}
 
return inertia('error/page', [
'status' => $status,
'message' => $this->messages[$status],
])
->toResponse($request)
->setStatusCode($status);
}
}

In the example above, we match error statuses with corresponding messages on the backend, but you are free to only pass the status prop and handle messages on the frontend.

You may want to keep the default Ignition-powered modal locally so that you can trace back the exceptions. To bypass a custom handler on local or testing environments, you can use the following condition before returning an Inertia response:

if (app()->environment(['local', 'testing'])) {
return $response;
}

Here’s an example error page component, which you can customize to fit your design. It accepts status and message props coming from the backend.

<script setup lang="ts">
defineProps<{
status: number
message: string
}>()
</script>
 
<template>
<div>
<h1>Error {{ status }}</h1>
 
<p>{{ message }}</p>
</div>
</template>

While it makes sense to display full pages on actual page visits (GET requests), you may want to notify the user with a toast message on action-caused exceptions, including POST, PUT or DELETE requests, and keep the current page's state preserved.

public function render($request, Throwable $e)
{
$response = parent::render($request, $e);
 
$status = $response->getStatusCode();
 
if (app()->environment(['local', 'testing'])) {
return $response;
}
 
if (! array_key_exists($status, $this->messages)) {
return $response;
}
 
if (! $request->isMethod('GET')) {
return back()
->setStatusCode($status)
->with('error', $this->messages[$status]);
}
 
return inertia('error/page', [
'status' => $status,
'message' => $this->messages[$status],
])
->toResponse($request)
->setStatusCode($status);
}

Please note that the approach requires a custom flash message handler. You may refer to the official documentation for the basic setup or learn the best way to handle toast notifications from Advanced Inertia.

Now, when a user navigates to a non-existing page or a page he doesn't have access to, the app will render a custom error page component. If a user submits a form and something goes wrong on the server, the browser will display a toast notification while preserving the current page and its state.

Use Inertia.js like a boss

Learn advanced concepts and make apps with Laravel and Inertia.js a breeze to build and maintain.

© 2023 Boris Lepikhin. All rights reserved.