The Next.js framework first introduced the pattern of Incremental Static (Re)Generation (commonly referred to as ISG) to the Jamstack community. With ISG, some URLs are rendered at build time, while others aren't rendered until a user actually visits the page (the app essentially falls back to server-side rendering). A static loading page is returned while server-side rendering is in progress. Individual statically rendered URLs can also be configured to expire after some time (this is the regeneration part).

Layer0 provides full support for Incremental Static (Re)Generation, not just on apps built with Next.js (where the @layer0/next package makes this automatic), but on apps built with any framework.

Developers using Next.js don't need to do anything special to support ISG on Layer0. Simply use the NextRoutes router plugin:

// routes.js

const { Router } = require('@layer0/core/router')
const { nextRoutes } = require('@layer0/next')

module.exports = new Router().use(nextRoutes)

To enable ISG on any framework, statically render a subset of your app's pages at build time as well as a static loading page. You can have a single loading page for your app or a separate one for each route. Then, configure your Layer0 router to serve the statically rendered pages using serveStatic. Use the onNotFound and loadingPage options to fall back to SSR while displaying the loading page when a request is received for a page that has not been statically rendered.

// The HTML route
router.get('/products/:id', ({ serveStatic, cache, renderWithApp }) => {
  cache({
    edge: {
      maxAgeSeconds: 60 * 60 * 24, // cache at the edge for 24 hours
    },
  })
  serveStatic('dist/products/:id.html', {
    // When the user requests a page that is not already statically rendered, fall back to SSR.
    onNotFound: () => renderWithApp(),

    // While SSR is in progress, display a static loading page.
    loadingPage: 'dist/products/loading.html',
  })
})

Your loading page will need to fetch the data to render the full page, so you'll likely need a data route as well, which can also be statically rendered. When a request has been received for data that has not been statically rendered, the system should block and wait for SSR to finish rather than returning a loading page (since the user is already seeing a loading page). For example:

// The data route
router.get('/api/products/:id.json', ({ serveStatic, cache, renderWithApp }) => {
  cache({
    edge: {
      maxAgeSeconds: 60 * 60 * 24, // cache at the edge for 24 hours
    },
  })
  serveStatic('dist/products/:id.json', {
    // When the user requests data that is not already statically rendered, fall back to SSR.
    onNotFound: () => renderWithApp(),
  })
})