How To Create An AdonisJS 5 & Inertia 1 Project with SSR

In this lesson, we’ll take a look at how to set up AdonisJS 5 and InertiaJS V1 using server-side rendering (SSR).

Published
Jul 23, 23
Duration
11m 31s

Developer, dog lover, and burrito eater. Currently teaching AdonisJS, a fully featured NodeJS framework, and running Adocasts where I post new lessons weekly. Professionally, I work with JavaScript, .Net C#, and SQL Server.

Adocasts

Burlington, KY

Creating Our AdonisJS Project

First, we’ll get a new AdonisJS project set up.

npm init adonis-ts-app@latest adonis-inertia-ssr

# CUSTOMIZE PROJECT
# ❯ Select the project structure · web
# ❯ Enter the project name · adonis-inertia-ssr
# ❯ Setup eslint? (y/N) · true/false
# ❯ Configure webpack encore for compiling frontend assets? (y/N) · true
Copied!

We’ll select web for our project structure so that it auto-includes the session and view packages. We’ll also include Webpack encore for our assets. Everything else is up to you.

The Adonis Inertia Provider

Next, we’ll need to install and configure the Adonis Inertia provider.

npm i @eidellev/inertia-adonisjs

# ❯ Enter the `.edge` view file you would like to use as your root template · app
# ❯ Would you like to use SSR? (y/N) · true
# ❯ Which client-side adapter would you like to set up? · Vue 3

# [ wait ]  Installing dependencies: @inertiajs/vue3, vue, @vue/server-renderer, webpa
# ck-node-externals .  
# CREATE: config/inertia.ts
# CREATE: resources/views/app.edge
# CREATE: start/inertia.ts
# CREATE: webpack.ssr.config.js
# UPDATE: .adonisrc.json { commands += "@eidellev/inertia-adonisjs/build/commands" }
# UPDATE: .adonisrc.json { providers += "@eidellev/inertia-adonisjs" }
# UPDATE: .adonisrc.json { preloads += "./start/inertia" }
# CREATE: ace-manifest.json file
Copied!

We’ll be using app as our Edge root file, make sure to set SSR to true, and we’ll be using Vue 3 here today.

This will stub some files into our application to get things going. Most notable is the addition of a webpack.ssr.config.js. This, as named, is our SSR configuration for Webpack.

Upgrading from an older InertiaJS version? Be sure to run through their upgrade guide to familiarize yourself with the changes.

Setting Up Our Vue Apps

Yes, apps… as in two apps. The key to getting SSR working is to have a separate entry point for our application. The SSR version will use Vue’s createSSRApp instead of the traditional createApp that our client side will use.

Client Side App Entry

import '../css/app.css'
import { createApp, h } from 'vue'
import { createInertiaApp } from '@inertiajs/vue3'

createInertiaApp({
  resolve: name => {
    const page = require(`./Pages/${name}`)
    return page
  },
  setup({ el, App, props, plugin }) {
    createApp({ render: () => h(App, props) })
      .use(plugin)
      .mount(el)
  },
})
Copied!
  • resources
  • js
  • app.js

Here we create our Vue app inside the Inertia setup. This allows Inertia to mutate our app as needed. Our Vue pages will reside within a Pages directory, and the resolve method is in charge of grabbing the correct file for the designated page name.

Server Side App Entry

Next, let’s create a new file at the same location called ssr.js. This will look fairly similar to our app.js, with some server-side twists 😱.

import { createSSRApp, h } from 'vue';
import { renderToString } from '@vue/server-renderer';
import { createInertiaApp } from '@inertiajs/vue3';

export default function render(page) {
  return createInertiaApp({
    page,
    render: renderToString,
    resolve: name => {
      const page = require(`./Pages/${name}`)
      return page
    },
    setup({ App, props, plugin }) {
      return createSSRApp({
        render: () => h(App, props),
      }).use(plugin);
    },
  });
}
Copied!
  • resources
  • js
  • ssr.js

First, the createInertiaApp is wrapped in a render method that’s provided on the page and the Inertia app is returned. Then, we’ll designate Vue’s server renderer as Inertia’s render method. Lastly, the setup method for Inertia is altered to create a Vue SSR app.

Configuring Webpack

Next, we’ll need to enable Vue and make a few tweaks to our SSR Webpack config.

Client-Side Webpack Config

First, within our webpack.config.js (this is our client-side config), scroll down to around line 184 and uncomment the Vue loader configuration.

Encore.enableVueLoader(() => {}, {
  version: 3,
  runtimeCompilerBuild: false,
  useJsx: false
})
Copied!

Server-Side Webpack Config

Next, within our webpack.srr.config.js (our ssr config), let’s uncomment the Vue loader configuration here as well. It should be around line 145.

Encore.enableVueLoader(() => {}, {
  version: 3,
  runtimeCompilerBuild: false,
  useJsx: false,
})
Copied!

Then, scroll down to the bottom of the file, and let’s alter the externals config.

--config.externals = [require('webpack-node-externals')()]
config.externals = [require('webpack-node-externals')({
  allowlist: ['@inertiajs/core', '@inertiajs/vue3'],
})]
Copied!

An important note is that Inertia V1 uses ESM. Without SSR, this wouldn’t be much of an issue, however, ESM is coming to AdonisJS in version 6 so, when the Inertia core and Vue 3 packages attempt to import on the server, we run into issues.

This externals config is excluding these two packages from our SSR bundle, allowing us to bypass this issue.

The alternative option is to stick to pre-V1 for Inertia via the legacy documentation, as those versions didn’t use ESM only.

Let’s Make A Page!

Next, let’s make a page to test this out!

<script setup>
  defineProps(['title'])
</script>

<template>
  <div>
    <h1>Home Page</h1>
  </div>
</template>
Copied!
  • resources
  • js
  • Pages
  • Index.vue

Define the inertia route, for this, we’ll just alter our welcome route.

Route.get('/', async ({ inertia }) => {
  return inertia.render('Index', { title: 'Test; })
})
Copied!
  • start
  • routes.ts

Let It Rip!

Lastly, to boot up our dev environment, we’ll have two different commands to run; one for our server-side script and another for the client side.

node ace ssr:watch  # get the server-side script going
npm run dev  # get the server booted & our client-side going
Copied!

Then, open your server in your browser. Be sure to inspect your document’s response. If SSR is working correctly, you’ll see your <h1>Home Page</h1> markup directly inside the raw response body. If it’s missing, something went wrong and your server-side rendering isn’t working.

TODO

Adding A Default Layout

Lastly, let’s add a default layout.

<script setup>
  import { Link } from '@inertiajs/vue3'
</script>

<template>
  <div>
    <nav>
      <Link href="/">Home</Link>
      <Link href="/about">About</Link>
    </nav>

    <main>
      <slot />
    </main>
  </div>
</template>
Copied!
  • resources
  • js
  • Layouts
  • App.vue

So, that’s our simple layout, let’s set it as our app’s default.

import '../css/app.css'
import { createApp, h } from 'vue'
import { createInertiaApp } from '@inertiajs/vue3'
import Layout from './Layouts/App.vue'

createInertiaApp({
  resolve: name => {
    const page = require(`./Pages/${name}`)
    page.default.layout = page.default.layout || Layout
    return page
  },
  setup({ el, App, props, plugin }) {
    createApp({ render: () => h(App, props) })
      .use(plugin)
      .mount(el)
  },
})
Copied!
  • resources
  • js
  • app.js

Lastly, and with it comes an important note, we need to duplicate this change over to our server-side app creation. Otherwise, our layout will only be rendered on the client side and not the server side.

import { createSSRApp, h } from 'vue';
import { renderToString } from '@vue/server-renderer'
import { createInertiaApp } from '@inertiajs/vue3'
import Layout from './Layouts/App.vue'

export default function render(page) {
  return createInertiaApp({
    page,
    render: renderToString,
    resolve: name => {
      const page = require(`./Pages/${name}`)
      page.default.layout = page.default.layout || Layout
      return page
    },
    setup({ App, props, plugin }) {
      return createSSRApp({
        render: () => h(App, props),
      }).use(plugin);
    },
  });
}
Copied!
  • resources
  • js
  • ssr.js

With that, our layout should now be included by default. Better yet, it’s also being rendered on the server, which we can confirm in our document’s network response.

TODO

Join The Discussion! (0 Comments)

Please sign in or sign up for free to join in on the dicussion.

robot comment bubble

Be the first to Comment!