Migrating Laravel / Vue App To Inertia JS

Photo by Ryu Orn on Unsplash

Migrating Laravel / Vue App To Inertia JS

·

5 min read

Problem Statement

Consider a Laravel application that started long ago, before Inertia JS came into existence. And the project might have already been using Vue or blade template to develop the user interface. But the complexity to build a perfect SPA (single page application) with only Vue and blade template is really frustrating.

Besides that, you might hear about Inertia JS or even, have worked with this technology and got impressed with the SPA feature that Inertia JS provides. It removes the complexity of state management on the front end of the application i.e Vue side. And you want to use this cool technology within your legacy project, but are afraid to integrate it because of the complexity of the project. There could be so many breaking changes that it might affect all of the features. Sometimes you might even need to upgrade dependencies as well.

So, in this article, I would like to give an idea in which we could migrate our old legacy Laravel Vue or blade-based project into Inertia JS-based SPA, without affecting any previous functionality.

Note: Our frontend part is written in Vue 2 and tailwind CSS 2, and I would like to write my new pages of the application in Vue 3 and Tailwind CSS 3.

Solution

So at first Let's check out a new branch in our project.

git checkout -b inertia-experiment

Let's create a new directory inertia in the root of the projects and go inside of it.

mkdir inertia & cd inertia

Initialize a new yarn project inside of it.

yarn init

After completion of the creation of the new yarn project, package.json file will be created inside of it. The next step would be adding vue 3 along with @vue/compiler-sfc and vue-loader as dependencies

yarn add vue@latest @vue/compiler-sfc vue-loader

and laravel-mix to compile the assets.

yarn add laravel-mix -D

After installing laravel mix, let's create a file webpack.mix.js in the current directory, in which you can write instructions to compile your assets, which looks like.

// webpack.mix.js
const mix = require("laravel-mix")
mix.css("css/app.css", "inertia/css")
    .js("js/app.js", "inertia/js").vue({ version: 3 })
    .sourceMaps()

The next step would be to create two folders js and css and create app.js file inside of js and app.css inside of CSS folder. Now let's install inertia js on client-side for Vue 3:

yarn add @inertiajs/inertia @inertiajs/inertia-vue3

and update recently created app.js file to initialize the client-side framework with the base Inertia component.

// app.js
import { createApp, h } from 'vue'
import { createInertiaApp } from '@inertiajs/inertia-vue3'
createInertiaApp({
  resolve: name => require(`./Pages/${name}`),
  setup({ el, App, props, plugin }) {
    createApp({ render: () => h(App, props) })
      .use(plugin)
      .mount(el)
  },
})

Now let's add some scripts in package.json to compile the assets as.

// package.json
"scripts": {
    "dev": "npm run development",
    "development": "mix",
    "watch": "mix watch",
    "watch-poll": "mix watch -- --watch-options-poll=1000",
    "hot": "mix watch --hot",
    "prod": "npm run production",
    "production": "mix --production"
},

The next step would be to add inertia js on the server side.

composer require inertiajs/inertia-laravel

Let's create a views folder inside our inertia folder and add a root template file app.blade.php

<!-- /inertia/views/app.blade.php -->
<!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>
    @inertiaHead
  </head>
  <body>
    @inertia
  </body>
</html>

Now publish a middleware

php artisan inertia:middleware

Instead of adding Inertia middleware to web middleware group, It is a good idea to apply only in inertia related route. Let's define a example route and apply the App\Http\Middleware\HandleInertiaRequests middleware to that route.

Route::middleware(HandleInertiaRequests::class)
    ->get("/inertia", function () {
        return Inertia::render("Agent/Home/Index");
    });

The remaining problem here is that we defined our root templates outsides of the Laravel default resources directory. So add the custom views path into Laravel view config.

// config/view.php
<?php
return [

    ....

    'paths' => [
        resource_path('views'),
        base_path('inertia/views')
    ],

    ....

Now we still have to configure the public paths of assets. Laravel can only access the public folder for assets, so we must produce our asset files into the public folder after compilation so. Let's update webpack.mix.js file and add mix.setPublicPath("../public")

// webpack.mix.js
const mix = require("laravel-mix")
mix.setPublicPath("../public")
mix.css("css/app.css", "inertia/css")
    .js("js/app.js", "inertia/js").vue({ version: 3 })
    .sourceMaps()

as the above configuration published our files into public/inertia/css and public/inertia/js directory so we need to update root template accordingly.

<!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('/inertia/css/app.css') }}" rel="stylesheet" />
    <script src="{{ mix('inertia/js/app.js') }}" defer></script>
    @inertiaHead
</head>
<body>
@inertia
</body>
</html>

Now we can add our application pages inside of Pages directory like

// inertia/js/Pages/Agent/Index.vue
<template>
    <div>
        Hello From Other sides.
    </div>
</template>
<script>
    export default {
        name: "Index",
    }
</script>

compile the project inside the inertia directory as:

yarn watch

and access the URL: {base}/inertia and you can see the text Hello From Other sides.

Conclusion:

This approach of migrating into Inertia JS is quite easy as we don't need to completely migrate the whole application. We can migrate into SPA one page at a time, secondly, the new inertia-related code is completely isolated from the previous Vue/blade components, so there wouldn't be any breaking changes in the system. No need to worry about dependencies as well.