next.js: On Vercel: On demand revalidation doesn't work for pages that are pre-rendered during build time
Link to the code that reproduces this issue
https://github.com/Javad9s/nextjs-revalidation-test
https://nextjs-revalidation-test.vercel.app/
To Reproduce
Deploy the project to Vercel and set this environment variables:
Env | Description | Example |
---|---|---|
NEXT_PUBLIC_SITE_URL | Necessary for internal api fetch | https://nextjs-revalidation-test.vercel.app |
CACHE_METHOD | Determines the cache method | fetch or fetch_isr or fetch_no_force or unstable_cache or unstable_cache_isr |
RENDER_LOG | Display custom logs in dashboard or console | true or false |
Special logs that are generated by RENDER_LOG are formatted like this: “### — HH:mm:ss — Log Text” and can be found in vercel dashboard.
If you see “Global Cache: api-error” on website because the internal api was not ready during build, deploy the project again and it should be ok.
Current vs. Expected behavior
1 - Behavior with fetch and cache: "force-cache"
(CACHE_METHOD = “fetch”):
-
Go to dynamic page (which will render in each hit). You should see something like “Global Cache: <266> 12-07 07:46:03”. This value is what we expect to see in all other pages [1].
-
Go to Control Panel. Then click on
revalidatePath("/", "layout")
[2]. -
Go to dynamic page again. You will see new global cache in dynamic page. You should see some logs like this in dashboard as well: “### — 08:24:58 — Called get-number api” and “### — 08:26:09 — Rendered dynamic page.tsx”
-
Go to other static pages and param pages. Notice pages that were previously cached during build process, have a hard time revalidating and you won’t see a log as well. Hence “/params/01” and “/params/02” will still show old data because they are rendered using
generateStaticParams()
. but other params (e.g. /params/03) will render again and show fresh data. -
In this stage if you somehow can cause a new render in those pages, they will revalidate and cache properly from this point forward (in another word they are cleaned). The workaround is you go to that page and hit reloads (ctrl + R). Keep in mind soft navigation and
router.refresh()
won’t cause a new render in this stage. If you are lucky enough, you can get one or two new renders in every step, then you click onrevalidatePath("/", "layout")
again and repeat the process until you clean all of the pages [3].
2 - Behavior with isr fetch (CACHE_METHOD = “fetch_isr”):
-
Same as 1.1
-
Same as 1.2
-
Same as 1.3
-
Most pages will render again properly here.
-
If a page is still serving old data, one reload (ctrl + R) will cause a new render, but keep in mind you will still see the stale page until next reload or soft navigation or
router.refresh()
[3]. -
For isr pages that have
prefetch = false
including “isr 10s” and “isr 2h”, first time navigating will cause a new render but new data will always be served in next visit [3].
Verify canary release
- I verified that the issue exists in the latest Next.js canary release
Provide environment information
Operating System:
Platform: win32
Arch: x64
Version: Windows 10 Pro
Binaries:
Node: 20.9.0
npm: N/A
Yarn: N/A
pnpm: N/A
Relevant Packages:
next: 14.0.5-canary.23
eslint-config-next: 14.0.4
react: 18.2.0
react-dom: 18.2.0
typescript: 5.3.3
Next.js Config:
output: N/A
Which area(s) are affected? (Select all that apply)
Not sure
Additional context
Side issues:
[1] Cache is not consistence between build on vercel and dynamic page
At first deploy, global cache is different between static pages and dynamic page. They serve a new and updated global cache while dynamic page is serving the one from previous build.
[2] Calling revalidatePath in a server action causes unnecessary render on the current page
This happens in local build as well. Control panel will render once, which will be lost and will not be cached. Next time you visit control panel you might still get old data or a different set of data. For this issue in local build, you can call revalidatePath(custom) and keep an eye on different values under “Page Specific” section in control panel (also keep in mind react, renders each page two times, one for hard reload and one for RSC and soft navigation).
[3] On Vercel: App router serves stale data despite rendering fresh pages
When attempting to revalidate with hard reloads. If you are lucky and caused a new render, at that specific hit you still get the stale data while you see the render log in the dashboard. Only After next reload or soft navigation or router.refresh()
new rendered page will be served. This behavior is extremely intense in “isr 10s” page.
About this issue
- Original URL
- State: open
- Created 6 months ago
- Reactions: 14
- Comments: 25 (8 by maintainers)
Hi, thanks again for the thorough repro, we have identified an issue with the fresh deployment case and are working on a fix. We will update here once it has been rolled/is available!
@Javad9s Thank you for putting together such a thorough test. I think the problem keeps getting overlooked because it is so intermittent.
@leerob I’ve spent literal days off and on over the past 4-6 months trying to get this to work. On new deployments there is almost always an issue with revalidation with static pages and generateStaticParams pages. Then after testing 3-4 times it starts to feel like it’s working (as if it needs to warm up or something), but the more you test you can still find cases of stale data that won’t update without another revalidation.
I also noticed it appears to be a little more reliable on a paid plan, but still not 100% reliable (and I’d be happy with 99%). Is there a priority thing going on with updates getting lost? Without the ability of revalidatePath/Tag returning a success or error object we can’t show you logs of it failing.
I want to get this to work because it feels like this is so much more efficient, and it’s been advertised for years now. Idk if anyone can show a more comprehensive test for the Nextjs team to see what we’re seeing.
I’m encountering issues with data revalidation using the revalidateTag option. I’m fetching the site name from the database to display it in sidebars and headers. However, when I modify the site name within the Settings form and use revalidateTag upon submission, the updated data is only reflected on the Settings page itself, not on other pages.
Facing this same issue as well on 14.1.0. If I remove
generateStaticParams
from the page, works as expected. However, if I usegenerateStaticParams
and revalidate the tag used on that page the cached page is hit resulting in showing stale data (after revalidation has occured).Same issue persists using 14.1.0. Works fine locally, but Vercel is not reliable.
Hey @valse
I was starting to think that too, but shouldn’t the expected behaviour be similar to
getStaticPaths
using thepages
router?Also, on local dev the ISR using
generateStaticParams
works fine, it’s just when deployed to Vercel the issue occurs. I can also getrevalidatePath
to work on Vercel sometimes however I can’t replicate repeatedly the conditions to make it work/not work. But I’m thinking therefore it is a current bug with Vercel/Next.js.I’m hoping this gets rectified soon otherwise I may have to start doing new projects using the
pages
router.Not Fixed, Just tested with 14.1.1-canary.1.
Hi @lmounsey, I tried a solution from a @leerob repo about revalidating static pages on demand and it works: the page is statically rendered, cached and revalidated.
You must add the
force-static
dynamic option, setdynanicParams
totrue
and returns an empty array from thegenerateStaticParams
method generating all the static pages at runtime.The issue for the pre-builded pages still exist but the result is quite the same 😅
having the same issue when revalidating the color of a theme. the theme provider is in the root layout of a
[domain]
path. everything works fine on the/
route but on all the sub routes the color stays the same after revalidation. As @alexknipfer mentinoned withoutgenerateStaticParams
everything works as expected.