Server-Side Rendering (SSR)
Server-side rendering pre-renders your JavaScript pages on the server, allowing your visitors to receive fully rendered HTML when they visit your application. Since fully rendered HTML is served by your application, it’s also easier for search engines to index your site.
Server-side rendering uses Node.js to render your pages in a background process; therefore, Node must be available on your server for server-side rendering to function properly.
InertiaCore Scaffolding
If you scaffolded your app with create-inertiacore, SSR support is already wired up — enable it by setting options.SsrEnabled = true in AddInertia(...) and running the SSR build script:
npm run build:ssrbuilder.Services.AddInertia(options =>{ options.SsrEnabled = true; options.SsrUrl = "http://127.0.0.1:13714/render";});Install Dependencies
If you are not using a Laravel starter kit and would like to manually configure SSR, we’ll first install the additional dependencies required for server-side rendering. This is only necessary for the Vue adapters, so you can skip this step if you’re using React or Svelte.
npm install vue-server-renderernpm install @vue/server-renderer// No additional dependencies required// No additional dependencies requiredThen, make sure you have the latest version of the Inertia Laravel adapter installed.
composer require inertiajs/inertia-laravelAdd Server Entry-Point
Next, we’ll create a resources/js/ssr.js file within our Laravel project that will serve as our SSR entry point.
touch resources/js/ssr.jsThis file is going to look very similar to your resources/js/app.js file, except it’s not going to run in the browser, but rather in Node.js. Here’s a complete example.
import { createInertiaApp } from '@inertiajs/vue2'import createServer from '@inertiajs/vue2/server'import Vue from 'vue'import { createRenderer } from 'vue-server-renderer'
createServer(page => createInertiaApp({ page, render: createRenderer().renderToString, resolve: name => { const pages = import.meta.glob('./Pages/**/*.vue', { eager: true }) return pages[`./Pages/${name}.vue`] }, setup({ App, props, plugin }) { Vue.use(plugin) return new Vue({ render: h => h(App, props), }) }, }),)import { createInertiaApp } from '@inertiajs/vue3'import createServer from '@inertiajs/vue3/server'import { renderToString } from '@vue/server-renderer'import { createSSRApp, h } from 'vue'
createServer(page => createInertiaApp({ page, render: renderToString, resolve: name => { const pages = import.meta.glob('./Pages/**/*.vue', { eager: true }) return pages[`./Pages/${name}.vue`] }, setup({ App, props, plugin }) { return createSSRApp({ render: () => h(App, props), }).use(plugin) }, }),)import { createInertiaApp } from '@inertiajs/react'import createServer from '@inertiajs/react/server'import ReactDOMServer from 'react-dom/server'
createServer(page => createInertiaApp({ page, render: ReactDOMServer.renderToString, resolve: name => { const pages = import.meta.glob('./Pages/**/*.jsx', { eager: true }) return pages[`./Pages/${name}.jsx`] }, setup: ({ App, props }) => <App {...props} />, }),)import { createInertiaApp } from '@inertiajs/svelte'import createServer from '@inertiajs/svelte/server'
createServer(page => createInertiaApp({ page, resolve: name => { const pages = import.meta.glob('./Pages/**/*.svelte', { eager: true }) return pages[`./Pages/${name}.svelte`] }, }),)When creating this file, be sure to add anything that’s missing from your app.js file that makes sense to run in SSR mode, such as plugins or custom mixins.
Setup Vite
Next, we need to update our Vite configuration to build our new ssr.js file. We can do this by adding a ssr property to Laravel’s Vite plugin configuration in our vite.config.jsfile.
export default defineConfig({ plugins: [ laravel({ input: ['resources/css/app.css', 'resources/js/app.js'], ssr: 'resources/js/ssr.js', // [!code ++] refresh: true, }), // ... ],})Update Npm Script
Next, let’s update the build script in our package.json file to also build our new ssr.js file.
"scripts": { "dev": "vite", "build": "vite build" // [!code --] "build": "vite build && vite build --ssr" // [!code ++]},Now you can build both your client-side and server-side bundles.
npm run buildRunning the SSR Server
Now that you have built both your client-side and server-side bundles, you should be able run the Node-based Inertia SSR server using the following command.
php artisan inertia:start-ssrWith the server running, you should be able to access your app within the browser with server-side rendering enabled. In fact, you should be able to disable JavaScript entirely and still navigate around your application.
Client Side Hydration
Since your website is now being server-side rendered, you can instruct
app.js file to use createSSRApp instead of createApp:app.js file to use hydrateRoot instead of createRoot:hydratable compiler option to true in your vite.config.js file:// No changes requiredimport { createApp, h } from 'vue' // [!code --]import { createSSRApp, h } from 'vue' // [!code ++]import { createInertiaApp } from '@inertiajs/vue3'
createInertiaApp({ resolve: name => { const pages = import.meta.glob('./Pages/**/*.vue', { eager: true }) return pages[`./Pages/${name}.vue`] }, setup({ el, App, props, plugin }) { createApp({ render: () => h(App, props) }) // [!code --] createSSRApp({ render: () => h(App, props) }) // [!code ++] .use(plugin) .mount(el) },})import { createInertiaApp } from '@inertiajs/react'import { createRoot } from 'react-dom/client' // [!code --]import { hydrateRoot } from 'react-dom/client' // [!code ++]
createInertiaApp({ resolve: name => { const pages = import.meta.glob('./Pages/**/*.jsx', { eager: true }) return pages[`./Pages/${name}.jsx`] }, setup({ el, App, props }) { createRoot(el).render(<App {...props} />) // [!code --] hydrateRoot(el, <App {...props} />) // [!code ++] },})import { svelte } from '@sveltejs/vite-plugin-svelte'import laravel from 'laravel-vite-plugin'import { defineConfig } from 'vite'
export default defineConfig({ plugins: [ laravel.default({ input: ['resources/css/app.css', 'resources/js/app.js'], ssr: 'resources/js/ssr.js', refresh: true, }), svelte(), // [!code --] svelte({ // [!code ++:5] compilerOptions: { hydratable: true, }, }), ],})You’ll also need to enable hydration in your app.js file:
import { createInertiaApp } from '@inertiajs/svelte'
createInertiaApp({ resolve: name => { const pages = import.meta.glob('./Pages/**/*.svelte', { eager: true }) return pages[`./Pages/${name}.svelte`] }, setup({ el, App, props }) { // [!code --:3] new App({ target: el, props }) }, setup({ el, App }) { // [!code ++:3] new App({ target: el, hydrate: true }) },})Deployment
When deploying your SSR enabled app to production, you’ll need to build both the client-side (app.js) and server-side bundles (ssr.js), and then run the SSR server as a background process, typically using a process monitoring tool such as Supervisor.
php artisan inertia:start-ssrTo stop the SSR server, for instance when you deploy a new version of your website, you may utilize the inertia:stop-ssr Artisan command. Your process monitor (such as Supervisor) should be responsible for automatically restarting the SSR server after it has stopped.
php artisan inertia:stop-ssrLaravel Forge
To run the SSR server on Forge, you should create a new daemon that runs php artisan inertia:start-ssr from the root of your app. Or, you may utilize the built-in Inertia integration from your Forge application’s management dashboard.
Next, whenever you deploy your application, you can automatically restart the SSR server by calling the php artisan inertia:stop-ssr command. This will stop the existing SSR server, forcing a new one to be started by your process monitor.
Heroku
To run the SSR server on Heroku, update the web configuration in your Procfile to run the SSR server before starting your web server.
web: php artisan inertia:start-ssr & vendor/bin/heroku-php-apache2 public/Note, you must have the heroku/nodejs buildpack installed in addition to the heroku/php buildback for the SSR server to run.