Revalidation in NextJS

Recently, I developed documentation site at my work. We had selected Sanity as our CMS and NextJS for front-end as I had an experience with NextJS which can help us develop and deploy quickly.

What is ISR(Incremental Site Generation)?

ISR is a process in which Server builds a static page on a given route at build time and than updates that static page on specific time interval to keep it fresh. And stores it at some location from which server can directly send that static page. So with the help of ISR server won’t have to generate page when user request which will lead to reduced latency to send content on client. Also you’ll have to send less JavaScript on client which less time to render content on client also.


What is revalidation?

Okay, So our pages have been generated at build time and have been stored on the server. Now after some time or whenever the content changes server needs to regenerate the page and update that static page with new content. So there are basically two types of revalidation :


On Demand Revalidation :

Whenever the backend or content changes we want to update the static page with new content. Usually this kind of revalidation is done by setting web-hook on the server. In simple words backend says to frontend :

“Hey, I am changed. Now change your-self“

How to do it?

While handling on demand revalidation in NextJS we have to manage two things one is data, and the second one is generated static pages.

For on demand revalidation to happen, we have to set up a certain route in NextJS which'll work as webhook. Now whenever the data changes in backend web-hook will be triggered and it'll come to the Next.js server. Now from this route we should define what route to invalidate.

Here is an example:

// app/blogs/actions.ts


/**
 * Get's an article by path
 */
export async function getArticleByUrl(path: string) {
  return await fetch(`<YOUR_BACKEND_API_ROUTE>?path=${path}`
    {
      next: {
        tags: [getArticleTag(path)],
      },
    }
  )
}

/**
 * This will be used to cache data.
 */
export function getArticleTag(path: string) {
  return `article:${path}`
}
// app/api/revalidate/route.js

import { NextResponse } from 'next/server';
import { revalidatePath, revalidateTag } from "next/cache"
import {getArticleTag} from "<path>/actions.ts"

export async function GET(request) {
  const { searchParams } = new URL(request.url);
  const secret = searchParams.get('secret');
  // Here I've used searchParams however, you can create mapping for different blogs with it's Id.
  const path = searchParams.get('path'); 

  // ✅ Check your secret key
  if (secret !== process.env.MY_SECRET_TOKEN) {
    return NextResponse.json({ message: 'Unauthorized' }, { status: 401 });
  }

  if (!path) {
    return NextResponse.json({ message: 'Path is required' }, { status: 400 });
  }

  try {
    // ✅ Trigger revalidation of the data
    revalidateTag(getArticleTag(path));

    // Now, that we have trigger revalidation of the data to the respective tag we'll
    // revalidate that respective pages/layout.

    // ✅ Trigger revalidation of the page
    revalidatePath(url, 'page')

    // ✅ Trigger revalidation of the layout,

    // Below function will only revalidate the layout which directly above that page.
    // For example: revalidating `/blogs/my-post` will affect `app/blogs/layout.tsx`
    // You can use conditions to not invalidate layout, if you don't want to.
    revalidatePath(url, 'layout')

    return NextResponse.json({ revalidated: true, path });
  } catch (err) {
    return NextResponse.json({ message: 'Error revalidating', error: err.message }, { status: 500 });
  }
}

Time Based Revalidation :

This revalidation is helpful whenever we are sure that content is getting updated on regular basis. So at specific time interval server will regenerate the content.

How to do it?

It is fairly easy to enable this kind of revalidation. Here is an example,


// Next.js will invalidate the cache when a
// request comes in, at most once every 60 seconds.
export const revalidate = 60

// We'll prerender only the params from `generateStaticParams` at build time.
// If a request comes in for a path that hasn't been generated,
// Next.js will server-render the page on-demand.
export const dynamicParams = true // or false, to 404 on unknown paths

For more details head to NextJS Docs. Above example is use for invalidating static sites only. Also, If you want to enable time-based invalidation you can do it via fetch function. As shown below,

/**
 * Get's an article by path.
 */
export async function getArticleByUrl(path: string) {
  return await fetch(`<YOUR_BACKEND_API_ROUTE>?path=${path}`
    {
      next: {
        tags: [getArticleTag(path)],
        revalidate: 60 // Revalidate every 60 seconds
      },
    }
  )
}

Which revalidation technique to use?

Answer to this question depends on your needs. If you’re sure that your data will keep updating no matter what then time-base revalidation is best suited to you. And if you using third-party service which is costing you tons then on-demand validation suites you best.

For my case, We were using third party(CMS - sanity) as our backend. So it should be cost effective and our marketing team will only update the data in sanity whenever needed (mostly on weekly basis). So, I was sure that I don’t need time-based revalidation as I pick on-demand revalidation.

1
Subscribe to my newsletter

Read articles from Himanshu Mendapara directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Himanshu Mendapara
Himanshu Mendapara