Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

jrenzi's avatar

Help trying to understand Inertia SSR issue in Laravel

Hey everyone. I'm new in this Laravel + Inertia + Vue + Tailwind + Node world, so I appreciate your patience.

In my current setup, Inertia is working perfectly. After setting up SSR, this is how I was running it.

php artisan inertia:start-ssr php artisan serve --port=3000 & npm run dev

I have also "hydratated" app.js.

When running it like this, this is what happens:

-I see a Flash of unstyled content (FOUC, new concept I just learned), where there's a split of a second where the CSS is not being loaded. -There are some styles and images that are not being loaded -There are lots of hydratation errors like this in the console:

[Vue warn]: Hydration node mismatch:
- rendered on server: <div class=​"relative hidden sm:​block mt-28">​…​</div>​  
- expected on client

Still, SSR is working. I tried lots of stuff and hair tearing to understand what was going on.

Then I checked the documentation again, and by running npm run build it did the job. No more hydratation problems and SSR working properly.

So, it would be great if someone could confirm if I'm getting all of this right:

  1. npm run build works because it needs to build all the pages, and this way SSR version works and matches both app.js and ssr.js versions?
  2. When developing the site, should I run it with npm run dev and set app.js to createApp, and then when building change it for createSSRApp?
  3. I'm trying to grasp the whole concept of hydratation. Does this mean that the page will load fully rendered in html, but still operative with js?
  4. When working with SSR, is the first page served as full html and the subsequent loads as js?

There are too many new concepts for me that I'm trying to figure out, so any concept clarification here is really appreciated :)

0 likes
8 replies
gych's avatar
gych
Best Answer
Level 29
  1. When running npm run build it will build your files for both server side and client side rendering. Server side rendering build is stored in bootstrap/ssr Client side rendering build is stored in public/build

  2. When developing the website you just run npm run dev. You can keep createSSRApp in app.js

  3. Correct, to get a better idea you can visually test this and disable javascript in your browser. Then you'll see how it acts without javascript as like hydration is disabled.

  4. All pages will be served first from the SSR (if not excluded), the hydration will let js work on those pages.

I hope this helps to clear it a bit up, if you have more questions don't hesitate to ask :)

1 like
Wembley's avatar

@gych Thanks! Very helpful, cause I cant understand why this hydration error was occurred. But when I disabled JS on page I'm not saw anything changed. And also I got a question about hydration - I'm know something about this when I'm worked with livewire, but I dont understand anything what is this and for what it needed. What the problem it solve? What a goals of hydration? Thanks for response!

gych's avatar

@Wembley You're welcome, I'm glad it was helpful to you. The goal of client-side hydration is to make your server-side rendered page interactive without having to re-render all the HTML again. It brings the static page back to life.

1 like
jrenzi's avatar

Thanks for the answer @gych.

  1. The problem is that if I set createSSRApp, then the page isn’t properly rendered. There are some images missing, and some stylings like big-white are ignored. Any idea why this might be happening?
  2. How do you exclude pages from SSR?
gych's avatar

@jrenzi

  1. No idea, I've never had this issue, can you share your app.js file code ?

  2. To exclude pages from SSR you can use a custom HTTPGateWay

Create a file called InertiaHttpGateway.php in for example app/Inertia with this content

namespace App\Inertia;

use Illuminate\Support\Str;
use Inertia\Ssr\HttpGateway;
use \Inertia\Ssr\Response;

class InertiaHttpGateway extends HttpGateway
{
    public function dispatch(array $page): ?Response
    {
        if (isset($page['url']) && Str::is('/singleroute', $page['url'])) return null;
        if (isset($page['url']) && Str::is('/admin*', $page['url'])) return null;

        return parent::dispatch($page);
    }
}

In the example above you can see that I added two example routes

  • /singleroute will exclude the specified url path from ssr
  • /admin* will exclude all url path's that start with /admin

Once this file is created add the created HttpGateway to the AppServiceProvider

use App\Inertia\InertiaHttpGateway;
use Inertia\Ssr\HttpGateway;

class AppServiceProvider extends ServiceProvider
{
    public $bindings = [
        HttpGateway::class => InertiaHttpGateway::class,
    ];

	// rest of existing code in AppService Provider
}

This is an alternative method but I personally prefer to use the HttpGateway

 @php
       if(Request::is('admin/*')){
           $__inertiaSsrDispatched = true;
           $__inertiaSsrResponse = null;
       }
    @endphp
3 likes
jrenzi's avatar

Here's my app.js

import './bootstrap';
import '../css/app.css';

import { createSSRApp, h } from 'vue';
import { createInertiaApp } from '@inertiajs/vue3';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m';

const appName = import.meta.env.VITE_APP_NAME || 'Laravel';

createInertiaApp({
    title: (title) => `${title} - ${appName}`,
    resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),
    setup({ el, App, props, plugin }) {
        return createSSRApp({ render: () => h(App, props) })
            .use(plugin)
            .use(ZiggyVue)
            .mount(el);
    },
    progress: {
        color: '#4B5563',
    },
});

Why do you prefer HTTPGateWay method? The other approach looks much simpler

gych's avatar

@jrenzi I don't like to add to much extra code in my app.blade file that in my opinion doesn't really belong there and can be handled elsewhere. I prefer to keep it clean therefor the gateway is the best solution for me.

I don't see anything in your app.js file that could cause this behavior, I always keep createSSRApp in my app js file and never had these issues.

Are you using web pack (mix) or vite?

Please or to participate in this conversation.