Laravel Jetstream with Inertia.js not recognizing changes

Published 11 September 2020 14:23 (6-minute read)

When developing your project using Laravel Jetstream and are using Inertia.js, you have to version your HTTP requests. This will force the front-end to refresh the page and all static assets like javascript/css.

To force Inertia.js to reload all static assets, can use the version method (for example, in your AppServiceProvider):

Inertia::version('random-string-to-identify-this-unique-application-version');

After you added the string to your application it will include this as a header in the responses made by Inertia (so, not the first request you make to the application - only the following requests made within your Inertia application).

When users are using your application and you deploy a new version, they have to visit a new page to force a fresh page request.

Laravel Mix and Laravel Jetstream with Inertia

When you are using a service like Laravel Mix to build your front-end assets and use asset versioning (this toggle cache-busting), you have a file that contains a list of all static assets in your application. This file is generated by Laravel Mix based on an md5 hash of the content of that unique file.

This is how your webpack.mix.js file may look like when using Laravel Mix versioning.

const mix = require('laravel-mix');

mix.js('resources/js/app.js', 'public/js')
    .postCss('resources/css/app.css', 'public/css', [
        require('postcss-import'),
        require('tailwindcss'),
    ])
    .version(); // < --- added

Then you can use the file "public/mix-manifest.json" to identify changes to your front-end assets to enforce loading new assets.

Note: when you register the Inertia::version in the AppServiceProvider, it will load Inertia any time when. For example, also when you run it via console. Do you want it only to run on web-requests? Take a look at the middleware way of implementing.

The "Middleware"-way of Inertia version

The new recommended way of adding the Inertia version to the requests is by a middleware. You can do this by creating a custom middleware that you add to the requests which need an Inertia version.

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File; // <-- added
use Inertia\Inertia; // <-- added

class InertiaVersionMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        if(File::exists($mixManifestFile = public_path('mix-manifest.json'))) {
            Inertia::version(function () use ($mixManifestFile) {
                return md5_file($mixManifestFile);
            });
        }
        
        return $next($request);
    }
}

Next, you have to register the middleware you the routes. It can be globally registered on any HTTP requests based on the "web" middleware group or for specific routes. You can do it by adding the created middleware in the HTTP Kernel.

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    // ..

    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Laravel\Jetstream\Http\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,

            \App\Http\Middleware\InertiaVersionMiddleware::class, // <-- added
        ],

        // ..

    ];

    // ..

}

It's also possible to take the advantage of the new PR that adds the versioning header: github.com/inertiajs/inertia-laravel/pull/161 & github.com/laravel/jetstream/pull/327.

The "AppServiceProvider"-way of Inertia version

Take a look at the AppServiceProvider.php boot method. It checks if the mix-manifest.json exists in the public folder. If so, it will load the content and make an md5 hash of the file' content, and set it as the Inertia version:

<?php

namespace App\Providers;

use Illuminate\Support\Facades\File; // <-- added
use Illuminate\Support\ServiceProvider;
use Inertia\Inertia; // <-- added

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        if(File::exists($mixManifestFile = public_path('mix-manifest.json'))) {
            Inertia::version(function () use ($mixManifestFile) {
                return md5_file($mixManifestFile);
            });
        }
    }
}

Cache Inertia version for speed improvements

When you have a lot of files or a big mix-manifest.json file, it can take some time to check if the file exists, load the content, make a hash with md5, and set it as an Inertia version.

You can make a special cache key that will handle the Inertia version for you. This will speed up the application when you have a big mix-manifest.json. There are many ways to do this, but here's one of the possibilities you could implement.

// file: app/Console/Commands/InertiaVersionFlushCommand.php
<?php

namespace App\Console\Commands;

use App\Providers\AppServiceProvider;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\File;

class InertiaVersionFlushCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'inertia:version-flush';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Make a unique Inertia version for this build based on the public/mix-manifest.json file.';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $mixManifestFile = public_path('mix-manifest.json');

        // check if the mix manifest exists
        if(!File::exists($mixManifestFile)) {
            $this->error("Mix manifest cannot be found at `{$mixManifestFile}`.");
            return;
        }
        
        // generate the md5 hash of the file
        $inertiaVersionHash = md5_file($mixManifestFile);
        
        // store the md5 hash as a new Inertia version
        Cache::put(AppServiceProvider::getInertiaVersionCacheKey(), $inertiaVersionHash);
    }
}

And also, update the AppServiceProvider:

// file: app/Providers/AppServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\ServiceProvider;
use Inertia\Inertia;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Inertia::version(self::getInertiaVersionFromCache());
    }

    public static function getInertiaVersionCacheKey() {
        return 'inertia-version';
    }

    public static function getInertiaVersionFromCache() {
        return Cache::get(self::getInertiaVersionCacheKey());
    }
}

During the deployment of your application (for example on production), you can call the command and it will set the new Inertia version based on the md5 hash of mix-manifest.json. This is saved forever in the cache until you refresh it (or remove the cache key).

php artisan inertia:version-flush

Ps, if you are not running the command, it is not setting the Inertia version on the requests, this blocks the cache-busting of static files.

If you have any questions, don't hesitate to contact me.

Robin Dirksen
Robin Dirksen

Follow me on Twitter, there I post web-related content, tips/tricks, and other interesting things.

On my blog, you can find articles that I've found useful or wanted to share with anyone else.

If you want to know more about this article or just want to talk to me, don't hesitate to reach out.

Webmention by Famdirksen
Famdirksen mentioned on 2nd October 2020
Legal