#No getStaticProps in the App router?

1 messages · Page 1 of 1 (latest)

unreal iris
#

So, there's no getStaticProps in the app router, ok, so you can use ISR then if you want to generate a static page, and have it revalidate every so many seconds.

Problem is though.... that only works WITH fetch requests...? .... so what if I have local data, like a DB or files im reading. I just have to use force-dynamic? but what if I want it to generate a static page, hold it for 60s, then regenerate...?

broken pawnBOT
#

Hi @unreal iris. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

You are correct: in the App Router, there is no getStaticProps. Instead, data fetching and caching are controlled at the fetch/request level, and static generation with revalidation (ISR) is typically achieved using the revalidate option—most commonly with fetch requests. For example:

export const revalidate = 60;

export default async function Page() {
  const data = await fetch('<https://...',> { next: { revalidate: 60 } });
  // ...
}

How to implement Incremental Static Regeneration (ISR)

What about local data (DB/files)?

You are not limited to fetch for ISR. Next.js provides the unstable_cache utility, which allows you to cache the result of any async function—including database queries or file reads—and set a revalidation interval. Here’s how you can use it:

import { unstable_cache } from 'next/cache';

const getCachedData = unstable_cache(
  async () => {
    // Your DB or file read logic here
    return getDataFromDBorFile();
  },
  ['cache-key'], // cache key
  { revalidate: 60 } // revalidate every 60 seconds
);

export default async function Page() {
  const data = await getCachedData();
  // ...
}

Caching and Revalidating

This approach allows you to generate a static page, cache it, and have it revalidate every 60 seconds, even if your data source is not a fetch request.

You do not need to use force-dynamic for this use case. force-dynamic is only necessary if you want the page to be rendered on every request (i.e., no caching at all), which is not what you want.

#

Summary:

  • Use unstable_cache for local data (DB/files) with revalidation.
  • You can achieve ISR for any async data source, not just fetch.
  • No need for force-dynamic unless you want no caching.

Let me know if you need a more detailed example!

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@unreal iris Kapa.ai is still learning and improving, please let me know how I did by reacting below.

unreal iris
#

@broken pawn got it, got it. so basically, its either
A.) fetch
B.) unstable_cache

and maybe a C.) "use cache" if thats still a thing?

broken pawnBOT
#

Let me check the knowledge base for you again. Give me one moment! :books:

#

Yes, your summary is accurate for the App Router in Next.js:

A.) fetch
You can use fetch with caching and revalidation options (e.g., next: { revalidate: 60 }) to enable ISR for data fetched from external APIs. This is the most common pattern for static generation and revalidation in the App Router How to implement Incremental Static Regeneration (ISR).

B.) unstable_cache
For local data sources (like databases or files), you can use unstable_cache to cache the result of any async function and set a revalidation interval. This allows you to achieve ISR-like behavior for non-fetch data sources Caching and Revalidating.

C.) "use cache"
Yes, "use cache" is an experimental feature (currently in canary) that allows you to mark a route, component, or function as cacheable. It can be used similarly to unstable_cache, but with a more declarative syntax. You enable it via the experimental.useCache flag in your next.config.js. It is intended to eventually replace unstable_cache use cache.

So, all three are valid approaches, with "use cache" being the newest and still experimental. For production, stick with fetch and unstable_cache unless you are comfortable with canary features.

Let me know if you want code examples for any of these!

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@unreal iris Kapa.ai is still learning and improving, please let me know how I did by reacting below.

unreal iris
#

@broken pawn well, what's a bit annoying is I had to make my own unstable_cache, called next-quick-cache, because unstable_cache didn't do ANY deduplication of requests (multiple people hitting invalidate cache at once, spawns a new process for each one to regenerate it when stale)

So this behavior wont work for my custom implementation? How can I make it so it does.. is that even possible?

broken pawnBOT
#

Let me check the knowledge base for you again. Give me one moment! :books:

#

I'm sorry, but the knowledge sources do not provide explicit details about how to implement deduplication of cache revalidation in a custom cache handler for Next.js, nor do they describe the internal deduplication behavior of unstable_cache beyond its default usage.

From the available sources, we know:

  • The default unstable_cache is designed to cache async function results and supports revalidation, but there are reports that it can trigger multiple revalidations if multiple requests arrive when the cache is stale, as you described. This is noted as a limitation or bug in some discussions, but no official solution or API for deduplication in custom cache handlers is documented see discussion.
  • There is no documentation on how to implement request deduplication logic in a custom cache handler, nor is there an official API or contract for this behavior in the custom handler interface see cache handler docs.
  • Some issues mention that deduplication (or memoization) is handled by React's cache or by fetch memoization during a single render pass, but this does not extend to cross-request deduplication or to custom cache handlers deduplication and memoization confusion.

In summary:
Based on the knowledge sources, there is no documented way to implement cross-request deduplication of cache revalidation in a custom cache handler for Next.js. If you need this behavior, you would need to implement your own locking or deduplication mechanism at the application or cache layer, but the official docs do not provide guidance or APIs for this.

#

If you need more details or want to discuss possible approaches, let me know!

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@unreal iris Kapa.ai is still learning and improving, please let me know how I did by reacting below.

unreal iris
#

@broken pawn well how do I get my cache implementation to play nicely with ISR?

broken pawnBOT
#

Let me check the knowledge base for you again. Give me one moment! :books:

#

To ensure your custom cache implementation works well with ISR (Incremental Static Regeneration) in Next.js, you need to follow the documented cache handler interface and behaviors expected by Next.js.

Key steps:

  1. Implement the Required Methods:
    Your cache handler should implement at least get, set, revalidateTag, and optionally resetRequestCache. The set method should store not just the value, but also metadata like lastModified and tags, as shown in the official example:

    // cache-handler.js
    const cache = new Map()
    
    module.exports = class CacheHandler {
      constructor(options) {
        this.options = options
      }
    
      async get(key) {
        return cache.get(key)
      }
    
      async set(key, data, ctx) {
        cache.set(key, {
          value: data,
          lastModified: Date.now(),
          tags: ctx.tags,
        })
      }
    
      async revalidateTag(tags) {
        tags = [tags].flat()
        for (let [key, value] of cache) {
          if (value.tags.some((tag) => tags.includes(tag))) {
            cache.delete(key)
          }
        }
      }
    
      resetRequestCache() {}
    }
    

    Configuring Caching

  2. Disable In-Memory Caching:
    In your next.config.js, set cacheMaxMemorySize: 0 to ensure only your custom handler is used:

    module.exports = {
      cacheHandler: require.resolve('./cache-handler.js'),
      cacheMaxMemorySize: 0,
    }
    

    Custom Next.js Cache Handler

#
  1. Persist and Share Cache Across Instances:
    Store your cache in a shared, durable storage (like Redis or S3) if you are running multiple instances, so all instances see the same cache and ISR works as expected across your deployment Configuring Caching.

  2. Follow the Data Structure:
    When storing cache entries, ensure you return an object with at least value, lastModified, and tags. This is required for Next.js to properly manage cache revalidation and invalidation Configuring Caching.

  3. ISR and Revalidation:
    ISR will work as long as your cache handler correctly implements the above methods and structure. Next.js will call your handler to get/set cache entries and to revalidate as needed.

Limitations:
The knowledge sources do not provide details on advanced features like deduplication of concurrent revalidations. If you need deduplication, you would need to implement it yourself at the cache layer (e.g., using locks in Redis), as this is not handled by the documented Next.js cache handler interface.

Summary:

  • Implement the documented cache handler interface.
  • Store and retrieve the required metadata.
  • Use shared storage for multi-instance deployments.
  • ISR will work as long as you follow these requirements.

If you need more advanced features (like deduplication), you will need to build that logic into your cache backend, as the official docs do not cover this scenario.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@unreal iris Kapa.ai is still learning and improving, please let me know how I did by reacting below.

unreal iris
#

@broken pawn but that doesnt sound like when Next.js is generating the page it will actually regenerate it..... because it only knows unstable_cache and not mine?..

broken pawnBOT
#

Let me check the knowledge base for you again. Give me one moment! :books:

#

You're correct to be cautious here. According to the official documentation and examples, Next.js will use your custom cache handler for ISR and data caching if you configure it properly in your next.config.js (using the cacheHandler option). When you use features like unstable_cache or the built-in ISR mechanisms (such as revalidate), Next.js will call your custom cache handler's methods (get, set, revalidateTag, etc.) to store and retrieve cached data Configuring Caching Custom Next.js Cache Handler.

However, if you create your own custom caching logic (like your own "next-quick-cache") that operates outside of the documented Next.js cache handler interface, Next.js itself will not be aware of it and will not use it for ISR or revalidation. Next.js only interacts with the cache handler you provide via the documented interface.

So, to ensure that Next.js will actually regenerate pages and manage ISR as expected, you must implement your cache logic through the official cache handler interface and configure it in next.config.js. If you use your own cache system that is not registered as the cache handler, Next.js will not use it for ISR.

Summary:

  • Next.js will only use the cache handler you configure via cacheHandler for ISR and data caching.
  • If you use your own cache system outside of this interface, Next.js will not use it for ISR or revalidation.
  • To have Next.js regenerate pages as expected, implement your cache logic through the official cache handler interface.
#

If you need deduplication or other advanced features, you must build them into your custom cache handler, but it must still conform to the documented interface for Next.js to use it for ISR Configuring Caching.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@unreal iris Kapa.ai is still learning and improving, please let me know how I did by reacting below.

unreal iris
#

@broken pawn what a pain, alright, so make my own custom cache handler, and then just use it like unstable_cache or? the documentation shows a basic cache implementation, it doesnt really show how its used? do you just still use unstable_cache or?

broken pawnBOT
#

Let me check the knowledge base for you again. Give me one moment! :books:

#

Great question! According to the official Next.js documentation, when you implement a custom cache handler (as shown in the docs), you do not call your cache handler directly. Instead, you configure it in your next.config.js using the cacheHandler option. After that, you continue to use Next.js APIs like unstable_cache, fetch with caching options, or ISR features as usual in your application code.

How it works:

  • You implement and configure your custom cache handler.
  • Next.js internally uses your handler for all cache operations related to ISR, unstable_cache, and data caching.
  • In your code, you still use unstable_cache or fetch with revalidation options—not your cache handler directly.

Example flow:

  1. Configure your handler:
    // next.config.js
    module.exports = {
      cacheHandler: require.resolve('./cache-handler.js'),
      cacheMaxMemorySize: 0,
    }
    
  2. Use Next.js APIs as normal:
    import { unstable_cache } from 'next/cache';
    
    const getCachedData = unstable_cache(
      async () => {
        // Your DB or file logic
      },
      ['key'],
      { revalidate: 60 }
    );
    
    export default async function Page() {
      const data = await getCachedData();
      // ...
    }
    
    Your custom cache handler will be called by Next.js behind the scenes Configuring Caching unstable_cache.

Summary:
You do not use your cache handler directly. You still use unstable_cache (or other Next.js caching APIs), and Next.js will route those cache operations through your custom handler if it is configured properly.

#

If the documentation is unclear, this is the intended usage pattern as described in the official sources.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@unreal iris Kapa.ai is still learning and improving, please let me know how I did by reacting below.