Ryan S. Chiang

How to Use Incremental Static Regeneration (ISR) with NextJS 14 App Router

Updated

4 min read

Here's a quick tip on how to use Incremental Static Regeneration (ISR) with NextJS new App Router model.

If you're short on time, the trick to implementing ISR using the App Router is as follows:

jsx// app/blog/[slug]/page.tsx

export const revalidate = 60; // seconds

const Page: NextPage<{}> = () => {
    return <div>Hello World</div>;
}

export default Page;

Now I'll dive into a short explanation and provide further tips for using ISR in the App Router.

What is Incremental Static Regeneration (ISR)?

ISR is mentioned explicitly in the docs of NextJS, but only in the Pages Router docs.

ISR, put simply, lets you build static pages after you've built your site.

Normally, you develop your NextJS app, build with next build, and start with next start.

But in many common use cases, like a NextJS blog for example, you may want to easily update or add new content without having to rebuild the site.

How does Incremental Static Regeneration work?

ISR is focused on the revalidation period.

Essentially, each time your app gets a request, NextJS will serve the static pages created at build time.

If you enable ISR on a page, when this page receives a request, NextJS will serve the cached static page as normal.

But if it's been longer than the revalidation period, NextJS will check whether the page data has been updated, and if so, it will rebuild this individual page, cache it, and continue to serve it as a static page.

How to Use ISR in NextJS

In the old Pages Router model, you would use getStaticProps to fetch data for your static pages.

jsx// pages/hello-world.tsx

export async function getStaticProps() {
    const postsDir = path.join(process.cwd(), "./posts");
    const posts = fs
        .readdirSync(postsDir)
        .filter((post) => post.endsWith(".md"));
 
    return {
        props: {
        posts,
        },
        // We enable ISR by setting `revalidate` in the return value of getStaticProps
        revalidate: 60, // seconds
    }
}

In the App Router model, we no longer use getStaticProps to fetch data. Instead, we fetch data in our page component directly.

Here's the same example but using the App Router:

jsx// app/hello-world/page.tsx

export const revalidate = 60; // seconds

const Page: NextPage<{}> = () => {
    const postsDir = path.join(process.cwd(), "./posts");
    const posts = fs
        .readdirSync(postsDir)
        .filter((post) => post.endsWith(".md"));

    return (
        <div>
            Hello World

            {/* Do something with `posts` */}
        </div>
    );
}

export default Page;

How long should the ISR revalidation period be?

While ISR is simple to implement, the only main consideration is how long of a revalidation period to set.

Let's take a common use case, where we have this basic site structure:

  • /blog - Blog index that lists all the articles on our blog
  • /blog/[slug] - Dynamic route for individual blog articles

In this example, it would make sense to add ISR to both pages.

By adding ISR to our blog index page, when we add a new blog post, our blog index will update with the new blog post.

Similarly, when adding ISR to our blog post page, when adding a new blog post, the dynamic route will properly serve the blog post.

The main consideration for the revalidation period is the frequency of data freshness we need.

For our blog, which isn't a realtime news site, we don't need high freshness.

So we could set our revalidate to 60 seconds, 5 minutes, or even 1 hour.

It's important to note that revalidation period for the blog index should be greater than or equal to the revalidation period of the individual blog page. That way, we won't face the UX issue where a user sees a blog post in the blog index, but upon navigating to it, they're met with a 404.

The other consideration for choosing a revalidation period is how intensive of data fetching you are doing.

In this case, we're only reading files from the filesystem. If you were performing more resource-heavy tasks in generating these static pages, a longer revalidation period would make sense.

For most cases, I've found 60 seconds to be a fine revalidation period. Start there and adjust to meet your app's specific needs.

Ryan Chiang

Meet the Author

Ryan Chiang

Hello, I'm Ryan. I build things and write about them. This is my blog of my learnings, tutorials, and whatever else I feel like writing about.
See what I'm building →.

If you want an alert when I write a new blog post (not often), you can subscribe below:

0

Comments

Leave a Comment

Your email will not be published. Required fields are marked *

2024

2023

© 2024 Ryan Chiang