Server-side rendering allows you to pre-render an initial page visit on the server, and to send the rendered HTML to the browser. This allows visitors to see and interact with your pages before they have fully loaded, and also provides other benefits such as decreasing the time it takes for search engines to index your site.
When Inertia detects that it's running in a Node.js environment, it will automatically render the provided page object to HTML and return it.
However, because most Inertia applications are built in languages such as PHP or Ruby, we'll need to hand the request over to a separate Node.js service so that it can render the page for us, and return the rendered HTML back to the browser when it's done.
First, we'll install the necessary server-side rendering dependencies using NPM or Yarn:
npm install @vue/server-renderer
yarn add @vue/server-renderer
We'll also install the @inertiajs/server
package, which provides a simple HTTP server. While this package isn't strictly necessary, it prevents the need to write your own HTTP service, and generally keeps things simple:
npm install @inertiajs/server
yarn add @inertiajs/server
Then, we'll create a resources/js/ssr.js
file:
touch resources/js/ssr.js
This file is going to look very similar like your app.js
file, with the exception that it's not going to run in the browser, but rather in Node.js. Here's a complete example:
import { createSSRApp, h } from 'vue'
import { renderToString } from '@vue/server-renderer'
import { createInertiaApp } from '@inertiajs/inertia-vue3'
import createServer from '@inertiajs/server'
createServer((page) => createInertiaApp({
page,
render: renderToString,
resolve: name => require(`./Pages/${name}`),
setup({ app, props, plugin }) {
return createSSRApp({
render: () => h(app, props),
}).use(plugin)
},
}))
app.js
file that makes sense to run in SSR mode, such as plugins or custom mixins. However, not everything needs to be included. For example, the InertiaProgress
library can be omitted from this file, as it will never be used in SSR mode.ssr.js
file as it won't help anything, given that we want to generate just one SSR build file. You can, of course, still use code splitting for your client-side build (app.js
).13714
. However, you can change this by providing a second argument to the createServer
method.PortalVue
package, it's import line MUST come after the import { createRenderer } from 'vue-server-renderer'
line.In order for our Webpack build to run properly on Node, we'll first need to install the webpack-node-externals
package:
npm install webpack-node-externals
Then, we'll create a new webpack.ssr.mix.js
file for SSR. This is necessary, because at the time of writing this, Laravel Mix does not support multiple webpack configurations within the same webpack.mix.js
file:
touch webpack.ssr.mix.js
Here is an example configuration for this file. Note that it will look much like your webpack.mix.js
configuration, with the exception that you only compile your JavaScript, and not your CSS.
Be sure to redefine any aliases used within your application. Using webpackConfig()
, be sure to set the target
to node
, and set externals
to [webpackNodeExternals()]
, which is the library we just installed.
const path = require('path')
const mix = require('laravel-mix')
const webpackNodeExternals = require('webpack-node-externals')
mix
.options({ manifest: false })
.js('resources/js/ssr.js', 'public/js')
.vue({ version: 3, options: { optimizeSSR: true } })
.alias({ '@': path.resolve('resources/js') })
.webpackConfig({
target: 'node',
externals: [webpackNodeExternals()],
})
Next, we'll need to make sure the @inertiaHead
directive is included in the <head>
section of our app.blade.php
file:
<!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>
Finally, we'll need to enable SSR in your application's inertia.php
configuration file. If you do not yet have this file in your application's config
folder, you should publish it first using the following command:
php artisan vendor:publish --provider="Inertia\ServiceProvider"
Then, enable SSR by setting the enabled
option under ssr
to true
:
<?php
return [
/*
|--------------------------------------------------------------------------
| Server Side Rendering
|--------------------------------------------------------------------------
|
| These options configures if and how Inertia uses Server Side Rendering
| to pre-render the initial visits made to your application's pages.
|
| Do note that enabling these options will NOT automatically make SSR work,
| as a separate rendering service needs to be available. To learn more,
| please visit https://inertiajs.com/server-side-rendering
|
*/
'ssr' => [
'enabled' => true,
'url' => 'http://127.0.0.1:13714/render',
],
// ...
You now have two build processes you need to run—one for your client-side bundle, and another for your server-side bundle:
npx mix
npx mix --mix-config=webpack.ssr.mix.js
Run both of these build steps and correct any errors that are generated. Remember, you're now building an "isomorphic" app, which means your app runs both on the client (browser) and on the server (Node).
With the builds generated, you should be able run the Node server using the following command:
node public/js/ssr.js
With that running, you should now 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 navigate around the app.
With this configuration, Vue will automatically try to "hydrate" the static markup and make it interactive, instead of re-rendering all the HTML that we just generated on the server. This is called "client side hydration". However, for client side hydration to work, the HTML generated on the server must be exactly the same as on the client, otherwise you'll see this warning in your console:
Of course, since you're generating the HTML from the same page components, this generally isn't an issue. However, if you do see this warning, see these caveats in the Vue 2 / Vue 3 SSR documentation.
[Vue warn]: Hydration children mismatch...
When deploying your SSR enabled app to production, you'll need to run both the client-side (app.js
) and server-side (ssr.js
) builds. One option here is to update the prod
script in package.json
to run both builds automatically:
"prod": "mix --production && mix --production --mix-config=webpack.ssr.mix.js",
To run the SSR server on Forge, create a new daemon that runs node public/js/ssr.js
in the root of your app.
Take note of the daemon ID that is generated, as you'll need to use this in your apps deployment script. Whenever you deploy your application, you'll need to automatically restart the SSR server. Add the following to your deployment script, updating "123456" with your daemon ID.
# Restart SSR server
sudo supervisorctl restart daemon-123456:daemon-123456_00
To run the SSR server on Heroku, update your web
configuration in your Procfile
to first run the SSR server before starting your web server. Note, to do this you must have the heroku/nodejs
buildpack installed in addition to the heroku/php
buildback.
web: node public/js/ssr.js & vendor/bin/heroku-php-apache2 public/