Inertia.js: Building Single-Page Apps the Old Way

#
By Gilbert Pellegrom

I’ve been building Laravel apps for years now. From large, data-heavy apps like Mergebot (RIP 😢) and SpinupWP to small side projects like my latest side project Formstatic. So when a new framework comes along that promises to make building Laravel apps much easier I’m more than happy to check it out. For the last while, I’ve been building my latest side project using a new up-and-coming framework by Jonathan Reinink called Inertia.js.

In this article, we’re going to take a look at what Inertia is, why you should consider using it and how you can get started using it in your next Laravel app.

What is Inertia.js?

Inertia calls itself “The Modern Monolith” and claims it allows you to build single-page apps (SPAs) without building an API. It does this by modeling itself on the classic server-side frameworks of old, from back in the heyday before JS ran the world. This means you don’t have to worry about much of the complexity that comes with building modern SPAs (e.g. client-side routing, APIs, etc).

The idea is that you write your web app as if it were a server-rendered app with full page loads. Just controllers and views. The difference is that your views are actually client-side components. When your app first loads, a full HTML page is returned. However, all subsequent page visits are done via AJAX (XHR) and return JSON.

Inertia converts your initial server-rendered HTML page into a SPA by passing a page object into a client-side app. This page object includes the necessary information required to render the page component (component, props, URL, etc.). Then, on subsequent page visits, requests are sent via AJAX and the response returns the same page object data but in JSON format. The server-side stuff is handled by middleware that uses headers to detect if the request is an Inertia request and if it should serve HTML or JSON. Pretty genius.

Why use Inertia.js?

To explain why you should consider using Inertia, it first helps to understand the current state of play when it comes to building web apps. Using SpinupWP (Laravel) as an example, we’ve built it using the kind of “hybrid” architecture Laravel comes with out-of-the-box. We have server-side controllers that render a blade view that contains a Vue component. The Vue component then interacts with the backend using an API which is a separate server-side controller that returns JSON responses.

The old architecture without Inertia

This architecture has a lot of pieces that all need to be maintained separately and even live in different places in the folder structure of the app. It’s not even a proper SPA either as we still need a separate full-page load for different pages in the app. This adds to the cognitive load and maintenance burden of the app.

The new architecture wit Inertia

Inertia, by contrast, allows us to build a more complete SPA with much fewer pieces. I’ve used Inertia to build my latest side project and it really is a breath of fresh air when it comes to simplifying the process of building web apps in Laravel.

How do I use Inertia.js?

Inertia is actually framework agnostic, meaning that it can work with almost any server-side framework (e.g. Laravel, Rails, etc.) and client-side framework (Vue, React, Svelte, etc.) through the use of adapters. For the purposes of this small example, I’m going to explain how to set up Inertia using Laravel and Vue.

To set up Inertia on the server-side, first, install the inertia-laravel adapter using composer:

composer require inertiajs/inertia-laravel

Next, we need to make sure we have an app.blade.php file that acts as a layout for our app. This template should include any assets, as well as the @inertia directive.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
    <link href="{{ mix('/css/app.css') }}" rel="stylesheet" />
    <script src="{{ mix('/js/app.js') }}" defer></script>
</head>
<body>
    @inertia
</body>
</html>

Finally, create the first controller and response. Instead of returning a view() like we normally would in Laravel to render a blade template we need to return Inertia::render() with the name of a front-end (Vue) component.

use Illuminate\Http\Request;
use Inertia\Inertia;

class BooksController extends Controller
{
    public function books(Request $request)
    {
        $books = $request->user()->books()->orderBy('created_at', 'desc')->paginate(10);

        return Inertia::render('Books', [
            'books' => $books,
        ]);
    }
}

To set up Inertia on the client-side, first, we need to install the inertia package and the inertia-vue adapter:

npm install @inertiajs/inertia @inertiajs/inertia-vue

Next, we need to set up Vue to use Inertia in our app.js file:

import { InertiaApp } from '@inertiajs/inertia-vue';
import Vue from 'vue';

Vue.use(InertiaApp);

const app = document.getElementById('app');

new Vue({
    render: h => h(InertiaApp, {
        props: {
            initialPage: JSON.parse(app.dataset.page),
            resolveComponent: name => require(`./Pages/${name}`).default,
        },
    }),
}).$mount(app);

Notice the require(./Pages/${name})? This is how we define where we load our Vue components. So, finally, create the Book.vue component in the ./Pages directory:

<template>
    <div>
        <h1>Books</h1>
        <ul>
            <li v-for="book in books.data">
                <inertia-link :href="`/books/${book.id}`">
                    {{ book.title }}
                </inertia-link>
            </li>
        </ul>
    </div>
</template>

<script>
export default {
    props: {
        books: Object
    }
}
</script>

There you have it! A full-blown SPA built using Laravel and Vue without having to even think about client-side routing and APIs.

Next Steps

As you can see, Inertia makes building modern SPAs as simple as building the old server-rendered web apps of the past. In my opinion, it really delivers on the promise of allowing you to create fully client-side rendered, single-page apps, without much of the complexity that comes with modern SPAs.

I’ve only scratched the surface of Inertia in this article. If you’re interested, I’d recommend having a good read through the Inertia docs to get familiar with the framework and how it works. One thing I’d highlight is that, if you’re using Laravel, it’s worth setting up Ziggy to generate named routes that can be used with the route() helper method in your front-end components.

If you’re interested in more of the backstory of Inertia, I can recommend this Full Stack Radio podcast where Adam Wathan speaks to Jonathan about why he decided to build the framework.

Have you ever built a SPA? Do you think you’d give Inertia a try? Got any other tips on building SPAs with Laravel and Vue? Let us know in the comments.

About the Author

Gilbert Pellegrom

Gilbert loves to build software. From jQuery scripts to WordPress plugins to full blown SaaS apps, Gilbert has been creating elegant software his whole career. Probably most famous for creating the Nivo Slider.