Benjamin Dudok.

Prerendering dynamic pages with vue-cli 3

When creating the blog you're currently reading, I tried prerendering my vue-powered website for the first time. It wasn't long untill I ran into the problem of trying to get dynamic pages rendered properly. I'll try and explain the end result of trying to solve an issue with prerendering dynamic page routes for this blog.

With vue-cli 3 being an abstraction layer over vue's webpack configuration, i started with searching for a vue-cli plugin for prerender-spa-plugin. Invoking vue-cli-plugin-prerender-spa is a breeze, and generally works perfect out of the box if you know all routes in your application on build-time.

Since this is a blog, there are a couple static pages, like the 'about' page. But there are also pages for each blog post. I don't want to keep track of a list of blog posts by hand. So i had to find a way to create an array of all pages in my blog to input in the prerender-spa renderRoutes option.

Since we're using vue-cli, we can configure webpack using a vue.config.js file. My vue.config file looked somewhat like this: module.exports = { pluginOptions: { prerenderSpa: { renderRoutes: [ '/', 'about' ], useRenderEvent: true, onlyProduction: true } } };

The key is to make a call to your headless cms, or file system, and obtain a list of your pages before compiling. But the vue.config.js file does not support asynchronous calls, so when you try and include an async call, vue will just go on and continue without waiting for the call to finish.

As explained in the issue, there's a way to circumvent this by creating your own command wrapper, where you can do your async stuff, and afterwards call the normal vue-cli build command. In this custom command you can just create an api call, and access the vue.config.js options to include your array of routes.

This is what my prerender command finally looked like (i'm using contentful as a headless cms):

var contentful = require('contentful'); module.exports = (api, options) => { api.registerCommand('build:prerender', async (args) => { const client = contentful.createClient({ space: process.env.VUE_APP_CONTENTFUL_SPACE, accessToken: process.env.VUE_APP_CONTENTFUL_API_KEY }); const entries = await client.getEntries({ content_type: 'post', select: 'fields.slug' }); options.pluginOptions = { prerenderSpa: { renderRoutes: [ '/', '/about', ...entries.items.map((i) => `/page/${i.sys.slug}`) ], useRenderEvent: true, onlyProduction: true } }; await api.service.run('build', args); }); }; module.exports.defaultModes = { 'build:prerender': 'production' };

Small note: hosting on netlify

Just a small heads up, when you intend to host your prerendered static site on netlify, make sure not include the headless: false|true option in your prerenderplugin settings. This will cause netlify's build to fail.