I'm building out a Shedquarters in my backyard! Check out the ever-evolving post + pictures here →

Using Ziggy with Inertia Server-Side Rendering

June 23, 2021

InertiaJS has recently opened up early support for server-side rendering. Currently it is in a beta release for GitHub supporters of the project, to be opened up to the public after things are ironed out.

While it's still early, the results are extremely promising. It's wicked fast and pretty easy to set up. There are a few gotchas that the team are still working out, one of which is popular Javascript libraries that rely on the window object, which is obviously not available when running on the server.

Ziggy

One of those popular libraries that people are having trouble with is Ziggy, which allows you use your Laravel routes in JavaScript.

It's an extremely helpful package that brings a Laravel-esque route helper to the frontend, but unfortunately it relies on the window object and a global Ziggy object as well. Fortunately it's a relatively easy fix.

Note: You will need at least version v1.3.0 of Ziggy for this to work!

Sending the Routes Over

The first thing we need to do is get all the routes into Ziggy. In a standard Laravel app this is done with the @routes Blade tag, which puts everything on the page. When using Inertia SSR you'll be rendering a Vue component, therefore the Blade tag isn't a viable option.

Ziggy exposes a couple of other options to get a hold of the routes, which is what we'll be using. We'll share it with Inertia using the Inertia::share functionality in HandleInertiaRequests middleware:

app\Http\Middleware\HandleInertiaRequests.php

1<?php
2 
3namespace App\Http\Middleware;
4 
5use Illuminate\Http\Request;
6use Inertia\Middleware;
7use Tightenco\Ziggy\Ziggy;
8 
9class HandleInertiaRequests extends Middleware
10{
11 ...
12 /**
13 * The root template that's loaded on the first page visit.
14 *
15 * @see https://inertiajs.com/server-side-setup#root-template
16 * @var string
17 */
18 protected $rootView = 'app';
19 
20 /**
21 * Determines the current asset version.
22 *
23 * @see https://inertiajs.com/asset-versioning
24 * @param \Illuminate\Http\Request $request
25 * @return string|null
26 */
27 public function version(Request $request)
28 {
29 return parent::version($request);
30 }
31 
32 
33 /**
34 * Defines the props that are shared by default.
35 *
36 * @see https://inertiajs.com/shared-data
37 * @param \Illuminate\Http\Request $request
38 * @return array
39 */
40 public function share(Request $request)
41 {
42 return array_merge(parent::share($request), [
43 // Add in Ziggy routes for SSR
44 'ziggy' => (new Ziggy)->toArray()
45 ]);
46 }
47}
Code highlighting powered by torchlight.dev.

Inertia will now add a ziggy key to your props, which will be passed to your SSR function.

Webpack Setup

Before this can work, you have to make one tweak to your webpack config (because of course you do, it's webpack.)

You need to tell webpack where to find the 'ziggy' import, because it's not a traditional NPM module, it's installed via composer.

webpack.ssr.mix.js

1// Mix V6
2mix.alias({
3 ziggy: path.resolve('vendor/tightenco/ziggy/src/js'),
4})
5 
6// Mix V5
7mix.webpackConfig({
8 resolve: {
9 alias: {
10 ziggy: path.resolve('vendor/tightenco/ziggy/src/js'),
11 },
12 }
13})

Configuration + Route Function

Inertia's SSR docs guide you through how to set up an ssr.js, which I won't cover here, but we will be modifying it a bit to include the new route helper.

Here's a basic SSR.js setup, with some of the not-yet-released SSR stuff omitted.

resources/js/ssr.js

1return await createInertiaApp({
2 // Some SSR stuff omitted...
3 setup({app, props, plugin}) {
4 return createSSRApp({
5 render: () => h(app, props),
6 })
7 },
8});

The first thing we need to do is import the route helper itself from Ziggy:

resources/js/ssr.js

1import route from 'ziggy';
2 
3return await createInertiaApp({
4 // Some SSR stuff omitted...
5 setup({app, props, plugin}) {
6 return createSSRApp({
7 render: () => h(app, props),
8 })
9 },
10});

We need to build up the Ziggy configuration manually, because it's not globally available to us like it is in the browser.

We'll pull ours off of the props passed to us from Inertia and add an additional location key, since there is no window.location on the server side.

(Ziggy added support for this specifically to help with Inertia SSR. Make sure you're on at least version 1.3.0.)

resources/js/ssr.js

1import route from 'ziggy';
2 
3return await createInertiaApp({
4 // Some SSR stuff omitted...
5 setup({app, props, plugin}) {
6 const Ziggy = {
7 // Pull the Ziggy config off of the props.
8 ...request.body.props.ziggy,
9 
10 // Build the location, since there is
11 // no window.location in Node.
12 location: new URL(request.body.props.ziggy.url)
13 }
14 
15 return createSSRApp({
16 render: () => h(app, props),
17 })
18 },
19});

Now instead of using their function as the mixin, we're going to use our own and add use the configuration object we just built, which gives us our final setup:

resources/js/ssr.js

1import route from 'ziggy';
2 
3return await createInertiaApp({
4 // Some SSR stuff omitted...
5 setup({app, props, plugin}) {
6 const Ziggy = {
7 // Pull the Ziggy config off of the props.
8 ...request.body.props.ziggy,
9 
10 // Build the location, since there is
11 // no window.location in Node.
12 location: new URL(request.body.props.ziggy.url)
13 }
14 
15 return createSSRApp({
16 render: () => h(app, props),
17 }).mixin({
18 methods: {
19 route: (name, params, absolute, config = Ziggy) => route(name, params, absolute, config),
20 },
21 })
22 },
23});

The final thing that I'd like to tackle is compiling the routes with the SSR build for production. In development it's probably fine, but some apps have a lot of routes, and sending those over the wire every time doesn't make a lot of sense. In production, it would make more sense to back the routes into the JS from the start.

I'll update this post once I get that sorted! Reach out to me at twitter.com/aarondfrancis if you have any thoughts!

Thanks for reading! My name is Aaron and I'm currently working at small property tax firm in Texas called Resolute Property Tax Solutions, where I serve in dual roles as COO & CTO.

I work on a lot of projects. I'm building a shedquarters. I currently do a podcast, and I used to do a different podcast.

If you ever have any questions or want to chat, I'm always on Twitter
Copyright 2013 - 2021, Aaron Francis.