On-Demand Incremental Static Regeneration for Next.js

This capability is relevant if you use Next.js in the Static Site Generation mode. If enabled, the business users don't have to wait for the full rebuild of their site to see the changes, which could take a while depending on the amount of content and how fast the build setup is.

While there are other strategies with ISR allowing to re-generate the page after a certain time, this on-demand mode is better since it allows to serve static pages as fast as possible and only re-generate if there are any content changes published in Uniform.

This baseline Next.js capability is described at length in this Next.js documentation here.

Check your specific CDN provider for compatibility

This capability is available on Vercel and if you self-host Next.js. If you are using other CDNs, make sure to consult the provider's documentation to ensure the on-demand ISR is supported. Uniform can work with any CDN provider that supports this feature.

The diagram below shows how the mechanics work together with Uniform and Next.js application:

On-demand ISR with Uniform and Next.js

First, we need to implement the handler for the on-demand revalidation to your Next.js codebase and make sure the rest of your Next.js app is compatible with this capability.

Component Starter Kit got you covered

If you are already using Component Starter Kit, good news - this handler is there out of the box, see this file for your reference.

  1. Make sure you set UNIFORM_PREVIEW_SECRET environment variable in your .env and your hosted site on Vercel or CDN you are using.

  2. Add api/revalidate.ts with the following code:

    import type { NextApiRequest, NextApiResponse } from 'next'; import { ProjectMapClient } from '@uniformdev/project-map'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { const secret = req.query.secret as string | undefined; if (secret !== process.env.UNIFORM_PREVIEW_SECRET) { return res.status(401).json({ message: 'Secret was not provided or it does not match' }); } const apiKey = process.env.UNIFORM_API_KEY; const projectId = process.env.UNIFORM_PROJECT_ID; if (!apiKey || !projectId) throw new Error('Uniform connection details are not provided.'); const pjmapClient = new ProjectMapClient({ apiKey, projectId, }); // Step 1: retrieving a composition by id from webhook payload const compositionId = (req.body?.composition_id as string | undefined) || (req.body?.id as string | undefined); if (!compositionId || typeof compositionId !== 'string') { return res.status(401).json({ message: 'Composition id is not provided' }); } // Step 2: retrieving all node paths to revalidate const { nodes } = await pjmapClient.getNodes({ compositionId }); const pathsToRevalidate: string[] | undefined = nodes?.map(n => n?.path); if (!pathsToRevalidate || pathsToRevalidate.length <= 0) { return res.status(404).json({ message: 'Paths could not be resolved for composition: ' + compositionId }); } try { console.log(`Revalidating paths: '${pathsToRevalidate}'`); // Step 3: for each path, issue a revalidate call await Promise.all( (pathsToRevalidate || []).map(async pagePath => { console.log(`Revalidating path: '${pagePath}'`); await res.revalidate(pagePath); }) ); console.log('Paths revalidated successfully.'); return res.json({ revalidated: true }); } catch (err: any) { // If there was an error, Next.js will continue // to show the last successfully generated page return res.status(500).json({ message: err.message, compositionId, query: req.query, body: req.body }); } }
  3. Locate your getStaticProps implementation and where you specify the value for client:

    export const getStaticProps = withUniformGetStaticProps({ ... client: getRouteClient(),
  4. Locate the code that instantiates the RouteClient and add disableSWR: true:

    const client = new RouteClient({ apiKey, projectId, disableSWR: true, });

    This is needed to ensure you are getting the freshest content from our CDN to avoid stale content from being used during on-demand revalidation.

    newer package version needed

    This settings is available with @uniformdev npm packages version 19.91.0 or higher, so consider updating your Uniform packages. If that is not an option, then use the following alternative with bypassCache: true, see below:

    const client = new RouteClient({ apiKey, projectId, bypassCache: true });

How in order for the webhook to be invoked after publishing, let's add it to your project.

  1. Head over to your Uniform project's Settings -> Webhooks.

  2. Add a webhook for the composition:published event and specify:

Webhook for on-demand ISR handler

Learn more about ISR for Next.js in Vercel's documentation.