Caching and Revalidating

Caching and revalidating data is also made very straightforward in Next.js 13. You can configure the caching and revalidating behavior on a per-page basis by utilizing a route segment.

Route Segment Config

The Route Segment options allows you to configure the behavior of a Page (opens in a new tab)Layout (opens in a new tab), or Route Handler (opens in a new tab) by directly exporting the following variables:

OptionTypeDefault
https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic (opens in a new tab)'auto''force-dynamic'
https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams (opens in a new tab)booleantrue
https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#revalidate (opens in a new tab)false'force-cache'
https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#fetchcache (opens in a new tab)'auto''default-cache'
https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#runtime (opens in a new tab)'nodejs''edge'
https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#preferredregion (opens in a new tab)'auto''global'

The two main options we are focusing on are dynamic and revalidate.

With these options, you can specify how a page should be rendered:

dynamic

  • force-static: This option is similar to using getStaticProps in the pages directory. It generates a static version of the page at build time or at runtime, which means the page content remains the same until the next build or the next revalidation. This is suitable for pages with data that doesn't frequently change.
  • force-dynamic: This option is similar to using getServerSideProps in the pages directory. It enables dynamic rendering of the page on each request, allowing you to fetch and manipulate data before rendering. This is suitable for pages with data that does frequently change.

revalidate

This option is similar to using getStaticProps in the pages directory. It generates a static version of the page at build time or at runtime, which means the page content remains the same until the next build or the next revalidation. This is suitable for pages with data that doesn't frequently change.

With these options, you can specify how a page should be rendered: either static using (just like pages (getStaticProps) or dynamic (getServerSideProps) or static with revalidation Incremental Static Regeneration (ISR) (opens in a new tab)

Examples

We will provide a brief overview to demonstrate caching, which is the most commonly used approach and covers the majority of basic caching use cases.

Static data

To make our page static with the data inside it, we simply need to add the dynamic route segment and set the value to force-static. This generates a static version of the page at build time.

export const dynamic = "force-static";
 
const data = [
  {
    title: "Title 1",
    body: "Lorem ipsum dolor sit amet."
  },
  {
    title: "Title 2",
    body: "Consectetur adipiscing elit, sed do eiusmod tempor incididunt."
  },
  {
    title: "Title 3",
    body: "Ut labore et dolore magna aliqua."
  },
  {
    title: "Title 4",
    body: "Sed ut perspiciatis unde omnis iste natus error."
  },
];
 
async function getData() {
  "use server"
  return data
}
 
export default async function Page() {
  const data = await getData()
 
  return (
    <div className="container mx-auto">
      <div className="grid grid-cols-2 gap-4">
        {data.map((item, index) => (
          <div
            key={index}
            className="bg-white p-4 shadow rounded-lg"
          >
            <h2 className="text-lg font-bold mb-2">{item.title}</h2>
            <p className="text-gray-700">{item.body}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

dynamic

To prevent data from being cached, we can add a dynamic route segment with a value of force-dynamic. This ensures that each page will be dynamic rendered and receive fresh data.

let's edit our getData() function to better demonstrate this example:

export const dynamic = "force-dynamic"
 
const data = [
  {
    title: "Title 1",
    body: "Lorem ipsum dolor sit amet."
  },
  {
    title: "Title 2",
    body: "Consectetur adipiscing elit, sed do eiusmod tempor incididunt."
  },
  {
    title: "Title 3",
    body: "Ut labore et dolore magna aliqua."
  },
  {
    title: "Title 4",
    body: "Sed ut perspiciatis unde omnis iste natus error."
  },
];
 
export async function getData(random?: boolean) {
  // shuffle the array on each request
  if (random) return shuffle(data)
  else return data;
}
 
function shuffle<T>(array: T[]): T[] {
  const shuffledArray = [...array];
  for (let i = shuffledArray.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]];
  }
  return shuffledArray;
}
 
export default async function Page() {
  const data = await getData(true)
 
  return (
    <div className="container mx-auto">
      <div className="grid grid-cols-2 gap-4">
        {data.map((item, index) => (
          <div
            key={index}
            className="bg-white p-4 shadow rounded-lg"
          >
            <h2 className="text-lg font-bold mb-2">{item.title}</h2>
            <p className="text-gray-700">{item.body}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

revalidation

We can add revalidate route segment to revalidate the data every x seconds

export const dynamic = "force-dynamic"
// this will update the cached data every 10 seconds
export const revalidate = 10

Next.js docs

I recommend reading the Next.js documentation (opens in a new tab) to learn more about route segments. Other options can be handy as well.

This is from the Next.js docs, explaining both options we mentioned earlier in detail.

dynamic

  • 'auto' (default): The default option to cache as much as possible without preventing any components from opting into dynamic behavior.
  • 'force-dynamic': Force dynamic rendering and dynamic data fetching of a layout or page by disabling all caching of fetch requests and always revalidating. This option is equivalent to:
    • getServerSideProps() in the pages directory.
    • Setting the option of every fetch() request in a layout or page to { cache: 'no-store', next: { revalidate: 0 } }.
    • Setting the segment config to export const fetchCache = 'force-no-store'
  • 'error': Force static rendering and static data fetching of a layout or page by causing an error if any components use dynamic functions (opens in a new tab) or dynamic fetches (opens in a new tab). This option is equivalent to:
    • getStaticProps() in the pages directory.
    • Setting the option of every fetch() request in a layout or page to { cache: 'force-cache' }.
    • Setting the segment config to fetchCache = 'only-cache', dynamicParams = false.
    • Note: dynamic = 'error' changes the default of dynamicParams from true to false. You can opt back into dynamically rendering pages for dynamic params not generated by generateStaticParams by manually setting dynamicParams = true.
  • 'force-static': Force static rendering and static data fetching of a layout or page by forcing [cookies()](https://nextjs.org/docs/app/api-reference/functions/cookies)[headers()](https://nextjs.org/docs/app/api-reference/functions/headers) and [useSearchParams()](https://nextjs.org/docs/app/api-reference/functions/use-search-params) to return empty values.

revalidate

  • false: (default) The default heuristic to cache any fetch requests that set their cache option to 'force-cache' or are discovered before a dynamic function (opens in a new tab) is used. Semantically equivalent to revalidate: Infinity which effectively means the resource should be cached indefinitely. It is still possible for individual fetch requests to use cache: 'no-store' or revalidate: 0 to avoid being cached and make the route dynamically rendered. Or set revalidate to a positive number lower than the route default to increase the revalidation frequency of a route.
  • 0: Ensure a layout or page is always dynamically rendered (opens in a new tab) even if no dynamic functions or dynamic data fetches are discovered. This option changes the default of fetch requests that do not set a cache option to 'no-store' but leaves fetch requests that opt into 'force-cache' or use a positive revalidate as is.
  • number: (in seconds) Set the default revalidation frequency of a layout or page to n seconds.