#Need to know what this is

1 messages · Page 1 of 1 (latest)

frigid elm
#

I’m experiencing a strange bug in a Next.js 15 App Router application.

[Bug Description]

  • On the page questions, the user edits some survey questions.
  • Then, they navigate to the /info page using a top AppBar button (<Link href=... />).
  • From the info page, they return to the questions page using useRouter().back().
  • When they return, the previously edited questions appear to be not updated — the page shows the old question state instead.

[Additional Info]

  • The /questions page is a Server Component (page.tsx) that fetches initial draft questions via getDraftQuestions(+id) which is DAL function and gets data using Prisma.
  • These draft questions are passed as initialData into a Client Component (builder.tsx), which manages state with useState.
  • From console logs, it looks like the server is not being called again, and the page shows stale data.
  • Interestingly, this bug does not happen when the user simply navigates within the /questions page and calls useRouter().back() — the state remains correct in that case.

[Questions]

  • What is the root cause of the stale data in this case?
  • Is this caused by browser-level BFCache (Back-Forward Cache) behavior after navigation between pages?
  • If so, what’s the recommended best practice in this kind of scenario to ensure the latest draft question data is shown?
    • Should I force a router.refresh() on mount?
    • Or is it better to move the draft question fetching entirely to the client side instead of relying on the Server Component?
glossy fieldBOT
#

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

#

Based on the knowledge sources, I can explain what's happening with your Next.js 15 App Router application and the stale data issue.

Root Cause: Client-side Router Cache Behavior

The issue you're experiencing is related to how Next.js 15 handles the client-side router cache, particularly during browser back/forward navigation.

In Next.js 15, page segments are not cached by default during regular navigation, but they are specifically reused during browser backward and forward navigation. This is documented in the Client-side Router Cache documentation:

Pages are not cached by default, but are reused during browser backward and forward navigation.

This explains why you're seeing stale data when using useRouter().back() to return to your questions page.

Not BFCache, but Next.js Router Cache

This is different from the browser's bfcache. As the documentation states:

This cache specifically applies to Next.js and Server Components, and is different to the browser's bfcache, though it has a similar result.

Solutions

Based on the knowledge sources, here are the recommended approaches:

1. Use router.refresh()

The most direct solution is to call router.refresh() when your component mounts after navigation. This will invalidate the Router Cache and make a new request to the server:

useEffect(() => {
  router.refresh();
}, []);

This approach is mentioned in several GitHub issues as a working solution, including issue #72408 where a Next.js team member confirms this approach.

2. Use staleTimes Configuration

You can adjust the caching behavior using the experimental staleTimes config option:

#
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    staleTimes: {
      dynamic: 0, // Set to 0 to disable caching for dynamic routes
    },
  },
}

module.exports = nextConfig

This was mentioned as a solution in issue #49300.

3. Move Data Fetching to Client Side

As you suggested, moving the data fetching entirely to the client side is another valid approach. This would involve:

  • Creating a client component that fetches data in a useEffect
  • Refreshing the data when the component mounts

This approach was suggested in issue #69979 by a Next.js contributor.

4. Use Server Actions with Cache Invalidation

If you're using server actions for data mutations, you can invalidate the cache using revalidatePath or revalidateTag within your server action, as mentioned in the Client-side Router Cache documentation.

Recommendation

The simplest and most reliable approach would be to use router.refresh() in a useEffect when your component mounts. This ensures fresh data without requiring configuration changes or architectural shifts.

If you're concerned about performance, the staleTimes configuration provides a more global solution that would apply to all similar scenarios in your application.

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

#

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

frigid elm
#

@glossy field I thought I used the revalidatePath but I didn't. I put it into the server action function and it works like a charm. Thanks.

glossy fieldBOT
#

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

#

I'm glad to hear that using revalidatePath in your server action resolved the issue! This is indeed one of the recommended approaches for handling cache invalidation in Next.js.

When you use revalidatePath in a server action, it invalidates both the Data Cache and the Client-side Router Cache in a single operation, ensuring that when you navigate back to the page, you'll see the fresh data instead of stale content.

As mentioned in the Next.js documentation, there are two ways to invalidate the Router Cache:

  1. In a Server Action:

    • Using revalidatePath or revalidateTag to revalidate data on-demand
    • Using cookies.set or cookies.delete to invalidate the Router Cache
  2. Calling router.refresh() to invalidate the Router Cache and make a new request to the server

Your solution of using revalidatePath in the server action is the most elegant approach since it handles the invalidation right at the source where the data is being modified.

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