This guide shows you how to configure Layer0 to prenderer pages to the edge cache to improve the performance of your site.

Layer0 allows you to specify the set of URLs that should be prerendered and cached at the edge during deployment to ensure that users get a subsecond experience when accessing your site. Static prerendering works by sending requests to your application code and caching the result right after your site is deployed. In this way, you simply build your app to implement server-side rendering and get the speed benefits of a static site for some or all of your pages. This feature is especially useful for large, complex sites that have too many URLs to prerender without incurring exceptionally long build times.

To specify which URLs should prerendered, use the Router's prerender function. The prerender function accepts an array of PrerenderRequest objects or an async function that returns the same:

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

module.exports = new Router().prerender([
  // HTML pages
  { path: '/' },
  { path: '/categories/mens' },
  { path: '/categories/mens/shirts' },
  { path: '/categories/mens/pants' },
  { path: '/categories/womens' },
  { path: '/categories/womens/shirts' },
  { path: '/categories/womens/pants' },

  // API responses
  { path: '/api/index.json' },
  { path: '/api/categories/mens.json' },
  { path: '/api/categories/mens/shirts.json' },
  { path: '/api/categories/mens/pants.json' },
  { path: '/api/categories/womens.json' },
  { path: '/api/categories/womens/shirts.json' },
  { path: '/api/categories/womens/pants.json' },
])

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

module.exports = new Router().prerender(async () => {
  const paths = await fetchCategoryPathsFromAPI()
  return paths.map(path => ({ path }))
})

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

module.exports = new Router().prerender(async () => {
  const paths = process.env.PRERENDER_PATHS.split(/\n/) // define the list of paths to prerender in the Layer0 Developer Console.
  return paths.map(path => ({ path }))
})

Layer0 can choose which pages to prerender based on site traffic, ensuring the most popular pages are always available in the edge cache.

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

router = new Router().prerender([
  {
    // the maximum number of pages that should be prerendered based on site traffic.
    top: 50,
  },
])

router = new Router().prerender([
  // Prerender with language cookie
  {
    top: 10,
    // Request headers that will be passed to your prerender request.
    // If you're splitting the cache by cookies or headers you can provide them
    // using headers option.
    headers: {
      cookie: 'language=en',
    },
  },
  // Prerender other language
  {
    top: 10,
    headers: {
      cookie: 'language=de',
    },
  },
])

It is important to prerender not just HTML responses, but API calls as well, to ensure that client-side navigation is as fast as possible. Some frameworks, such as Next.js, embed a build ID in API URLs to ensure that client receives responses from the correct version of the back end. In other frameworks the convention for how API URLs are structured is left to the developer.

const { Router } = require('@layer0/core/router')
const { nextRoutes } = require('@layer0/next')
const { existsSync, readFileSync } = require('fs')
const { join } = require('path')

// Read the Next.js build ID from '.next/BUILD_ID
const buildIdPath = join(process.cwd(), '.next', 'BUILD_ID')

function getPrerenderRequests() {
  const prerenderRequests = [
    { path: '/' },
    { path: '/categories/mens' },
    { path: '/categories/mens/shirts' },
    { path: '/categories/mens/pants' },
    { path: '/categories/womens' },
    { path: '/categories/womens/shirts' },
    { path: '/categories/womens/pants' },
  ]

  if (existsSync(buildIdPath)) {
    // Derive the API requests from the HTML page URLs
    const buildId = readFileSync(buildIdPath, 'utf8')
    const apiPaths = prerenderRequests.map(path => ({ path: `/data/${buildId}${path}.json` }))
    prerenderRequests.push(...apiPaths)
  }

  return prerenderRequests
}

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

If you're splitting the cache by cookies or headers using a CustomCacheKey, you'll need to include the cookie or header values in your preload configuration. For example, if you're splitting the cache by a language cookie:

const { Router, CustomCacheKey } = require('@layer0/core/router')

module.exports = new Router()
  .prerender([
    // German
    { path: '/categories/mens', headers: { cookie: 'language=de' } },
    { path: '/categories/mens/shirts', headers: { cookie: 'language=de' } },
    { path: '/categories/mens/pants', headers: { cookie: 'language=de' } },
    { path: '/categories/womens', headers: { cookie: 'language=de' } },
    { path: '/categories/womens/shirts', headers: { cookie: 'language=de' } },
    { path: '/categories/womens/pants', headers: { cookie: 'language=de' } },

    // English
    { path: '/categories/mens', headers: { cookie: 'language=en' } },
    { path: '/categories/mens/shirts', headers: { cookie: 'language=en' } },
    { path: '/categories/mens/pants', headers: { cookie: 'language=en' } },
    { path: '/categories/womens', headers: { cookie: 'language=en' } },
    { path: '/categories/womens/shirts', headers: { cookie: 'language=en' } },
    { path: '/categories/womens/pants', headers: { cookie: 'language=en' } },
  ])
  .get('/categories/:slug*', ({ cache }) => {
    cache({
      key: new CustomCacheKey().addCookie('language'),
      edge: { maxAgeSeconds: 60 * 60 * 24, staleWhileRevalidate: 60 * 60 * 24 * 365 },
    })
  })

By default, Layer0 prerenders a maximum of 200 URLs at a time. This can create significant additional load on your APIs at the time of deployment. You can lower this limit by setting the prerenderConcurrency property in layer0.config.js. Layer0 imposes the following limits on prerendering:

TierConcurrencyTotal number of requests
ENTERPRISE20025,000 per deployment
FREE10100 per deployment

When you deploy a new version of your site, you can view the progress and results of prerendering from the deployment view in Layer0 Developer Console:

progress

This section updates in real time as pages are prerendered and will show you any errors that occur. If an error occurs, more information can be found in the build logs.