Support the ongoing development of Laravel.io →

Create a custom error page with Laravel and Inertia

13 Aug, 2024 5 min read

Photo by Getty Images on Unsplash

How to display a custom error page with Inertia instead of Laravel’s default error pages.

Capsules Error Image 0

A sample Laravel project can be found on this Github Repository. Find out more on Capsules or X.

In a previous article, the focus was on customizing the 502 Bad Gateway error page servec by Nginx. This page was an HTML file placed directly in the /public folder. This article now focuses on customizing the error pages returned by Laravel in a VILT project.

To get a preview of the layout of Laravel’s default error pages, simply generate an error using the App::abort facade or the abort helper, followed by the desired error code.

routes/web.php

<?php

use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\App;


Route::get( '/', fn() => App::abort( 503 ) );

Capsules Error Image 1

Route::get( '/', fn() => App::abort( 419 ) );

Capsules Error Image 2

Route::get( '/', fn() => App::abort( 404 ) );

Capsules Error Image 3

By delving into the functionality of the abort function, it becomes clear that Laravel generates an Exception, which is then handled by the ExceptionHandler created by the withException method in the bootstrap/app.php file.

vendor/laravel/framework/src/Illuminate/Foundation/Application.php

public function abort($code, $message = '', array $headers = [])
{
    if ($code == 404) {
        throw new NotFoundHttpException($message, null, 0, $headers);
    }

    throw new HttpException($code, $message, null, $headers);
}

vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php

protected function prepareResponse($request, Throwable $e)
{
    if (! $this->isHttpException($e) && config('app.debug')) {
        return $this->toIlluminateResponse($this->convertExceptionToResponse($e), $e)->prepare($request);
    }

    if (! $this->isHttpException($e)) {
        $e = new HttpException(500, $e->getMessage(), $e);
    }

    return $this->toIlluminateResponse(
        $this->renderHttpException($e), $e
    )->prepare($request);
}

bootstrap/app.php

<?php

use Illuminate\Foundation\Application;

return Application::configure( basePath : dirname( __DIR__ ) )
    ->withRouting( web : base_path( "/routes/web.php" ) )
    ->withExceptions()
    ->create();
  • If the withExceptions method is removed from the file, everything falls apart. Therefore, it is crucial not to overlook this method.

To allow Inertia to intercept the various exceptions, it is necessary to modify the bootstrap/app.php file. This is primarily where the magic happens.

More specifically, regarding the withExceptions method : it handles exception interception. When the exception is an HttpException, which is the focus of this article, the Inertia rendering is triggered. The method $exceptions->render( fn( HttpException $exception, ... ) ) allows filtering of exception types. If the exception is an HttpException, the Inertia rendering process continues. Inertia then returns the Error page with the $request response and its status code.

bootstrap/app.php

<?php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Illuminate\Http\Request;
use Inertia\Inertia;


return Application::configure( basePath : dirname( __DIR__ ) )
    ->withRouting( web : base_path( "/routes/web.php" ) )
    ->withMiddleware()
    ->withExceptions( fn( Exceptions $exceptions ) =>

        $exceptions->render( fn( HttpException $exception, Request $request ) =>

            Inertia::render( 'Error', [ 'status' => $exception->getStatusCode() ] )
                   ->toResponse( $request )
                   ->setStatusCode( $exception->getStatusCode() )

        )
    )
    ->create();

It is then possible to customize the desired error pages by using a dedicated Vue component Error for errors.

resources/js/pages/Error.vue

<script setup>

import { Head } from '@inertiajs/vue3'
import { useStatusName, useStatusMessage } from '~/composables/status';
import logotype from '/public/assets/capsules-logotype.svg';


const props = defineProps( { status : Number } );

</script>

<template>

    <Head>

        <title>{{ useStatusName( status ) }}</title>

    </Head>

    <div class="h-screen flex justify-center">

        <div class="flex items-center space-x-10">

            <img class="h-20 w-20" v-bind:src="logotype">

            <div class="flex flex-col items-start space-y-2 font-mono text-primary-black">

                <h1 class="text-2xl text-primary-blue" v-text="`${props.status} - ${useStatusName( status )}`" />

                <p class="text-md" v-text="useStatusMessage( status )" />

            </div>

        </div>

    </div>

</template>
  • The Head component allows for customizing the <head> section of the HTML page. However, it is important not to forget to include @inertiaHead in the app.blade.php.

As well as a status composable that returns the desired error codes and messages.

resources/js/composables/status.js

export function useStatusName( status )
{
    switch( status )
    {
        case 503 : return 'Service Unavailable';
        case 500 : return 'Server Error';
        case 419 : return 'Session Expired';
        case 418 : return 'I\'m a teapot';
        case 404 : return 'Page Not Found';
        case 403 : return 'Forbidden';
        default : return 'Error';
    }
}

export function useStatusMessage( status )
{
    switch( status )
    {
        case 503 : return 'Sorry, we are doing some maintenance. Please check back soon.';
        case 500 : return 'Whoops, something went wrong on our servers.';
        case 419 : return 'Sorry, your session expired.';
        case 418 : return 'Sorry, I am not a coffee machine.';
        case 404 : return 'Sorry, the page you are looking for could not be found.';
        case 403 : return 'Sorry, you are forbidden from accessing this page.';
        default : return 'Sorry, something went wrong unexpectedly.';
    }
}

To test different exceptions directly from the browser :

routes/web.php

<?php

use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\App;

Route::get( '/{status?}', fn( int | null $status = null ) => App::abort( $status ?? 418 ) );
herd link article

npm run dev

> http://article.test/503

Capsules Error Image 4

> http://article.test/419

Capsules Error Image 5

> http://article.test/404

Capsules Error Image 6

Glad this helped.

Last updated 3 weeks ago.

driesvints liked this article

1
Like this article? Let the author know and give them a clap!
mho (MHO) Full time side project full stack web developer | designer Work @ http://capsules.codes

Other articles you might like

November 18th 2024

Laravel Custom Query Builders Over Scopes

Hello 👋 Alright, let's talk about Query Scopes. They're awesome, they make queries much easier to r...

Read article
November 19th 2024

Access Laravel before and after running Pest tests

How to access the Laravel ecosystem by simulating the beforeAll and afterAll methods in a Pest test....

Read article
November 11th 2024

🍣 Sushi — Your Eloquent model driver for other data sources

In Laravel projects, we usually store data in databases, create tables, and run migrations. But not...

Read article

We'd like to thank these amazing companies for supporting us

Your logo here?

Laravel.io

The Laravel portal for problem solving, knowledge sharing and community building.

© 2024 Laravel.io - All rights reserved.