How to implement Incremental Static Generation (ISG) with Layer0 and Nuxt.js
What is Incremental Static (Re)Generation?
For understanding Incremental Static Generation, let the explanation be guided by a scenario where a page requested by the user was not pre-rendered while statically exporting the website. In such a case, a fallback page is served while the data loads. Further, upon completion of building the page with the fetched data, the page is then cached onto the server. Any user requesting the page then would skip over the fallback and loading stages, and directly see the built page. Superb!
Regeneration is inspired by the concept of stale-while-revalidate, where stale data is refreshed at regular intervals of 'revalidate' seconds. For pages that have staleWhileRevalidateSeconds value configured, they would be re-built after regular intervals of the configured value.
This section will describe how to implement ISG with Layer0 and Nuxt.js. The steps include configuring a nuxt app with layer0, creating dynamic page and api routes, and finally using layer0 configurations to see ISG in action.
Configuring Nuxt.js app with Layer0
Create a Nuxt.js app by the following command:
npm create nuxt-app
For Choose custom server framework select None
For Choose rendering mode select Universal (SSR) In case you already have a nuxt app, just maintain the above configurations and would be good to go.
Add the Layer0 CLI via:
npm i -g @layer0/cli
In nuxt.config.js, add "@layer0/nuxt/module" to buildModules:
Nuxt makes it super easy to create dynamic pages. To set up a dynamic route, create a file
_slug.vue in some-route folder in pages directory of your app.
To obtain the dynamic parameter slug, nuxt provides a data fetching hook asyncData which has access to the context object. For example, in the following pages/some-route/_slug.vue dynamic page, one can obtain the slug via params.slug to make data fetching calls before the page gets rendered.
<template>
{{resp.some_data_key}}
</template>
<script>
export default {
mounted() {
// For a client side transition, fetch the page again to cache it on the edge
if (typeof window !== 'undefined') {
fetch(`/some-route/${this.slug}`)
}
},
async asyncData({ params, redirect }) {
let link = process.env.API_URL
if (typeof window !== 'undefined') link = window.location.origin
// set the origin to window in client side fetch, or use process.env.API_URL to get the deployment origin for server side fetch calls
let resp = await fetch(`${link}/api/some-route/${params.slug}.json`).then((res) => res.json())
return {
resp,
slug: params.slug
}
},
}
</script>
const express = require('express')
const app = express()
app.use(express.json())
app.all('/api/some-route/:slug.json', (req, res) => {
const slug = req.params.slug
// some data fetching calls from the slug
res.json({ data: 'data' })
})
module.exports = app
Layer0 ISG Goodies
For ISG, use routes.js (Created automatically by layer0 init command) and add route for the dynamic pages /some-route/_slug.vue and the dynamic api route /api/some-route/:slug.json as in the gist below:
// This file was added by layer0 init.
// You should commit this file to source control.
const { Router } = require("@layer0/core/router")
const { nuxtRoutes } = require("@layer0/nuxt")
module.exports = new Router()
.match("/service-worker.js", ({ serviceWorker }) => {
serviceWorker(".nuxt/dist/client/service-worker.js");
})
.get("/some-route/:slug", ({ cache }) => {
cache({
edge: {
maxAgeSeconds: 60 * 60 * 24 * 365, // keep the incrementally generated page for a year
},
browser: false,
})
serveStatic('dist/some-route/:slug.html', {
// When the user requests a page that is not already statically rendered, fall back to SSR.
onNotFound: () => renderWithApp()
})
})
.get('/api/some-route/:slug.json', ({ serveStatic, cache, renderWithApp }) => {
cache({
edge: {
maxAgeSeconds: 60 * 60 * 24, // cache at the edge for 24 hours
},
})
serveStatic('dist/some-route/:slug.json', {
// When the user requests data that is not already statically rendered, fall back to SSR.
onNotFound: () => renderWithApp(),
})
})
.use(nuxtRoutes)
Testing and Deployment
To test locally how the app would do in production, run the following:
layer0 build && layer0 run --production
To deploy run: layer0 deploy
Celebrate!🎉
Example of Incremental Static Generation with Layer0
With Layer0, it's easier than ever to implement Incremental Static Generation for different purposes with different frameworks. The following seeks to implement ISG with Nuxt.js via Layer0.