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.
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.
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.
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;
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 articlesIn 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.
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 →.
Thanks for reading! If you want a heads up when I write a new blog post, you can subscribe below: