Next.js 16.3 preview: "use cache" breaks i18next with Cannot access cookies() or headers() in "use cache"
Trying the instant navigation work on the preview channel, so npm install next@preview as of this week, cacheComponents: true in next.config.ts, and 'use cache' added to layouts and pages so the static shell gets prerendered.
Everything that touches i18next falls over, in two different shapes:
Shape 1, when the i18n setup resolves the locale from the request:
Cannot access
cookies()orheaders()in"use cache"
Shape 2, when I try to be clever and hand it a promise instead of an awaited value: the build sits there for about a minute and then dies with
Error: Filling a cache during prerender timed out, likely because request-specific arguments such as params, searchParams, cookies() or uncached data were used inside "use cache".
The annoying part is that my translations do not depend on the request at all. They are JSON bundles that could be resolved at build time and never change between users. The only per-request thing in the whole chain is which locale, and even that is in the URL.
So: what is the shape that actually works with cacheComponents on, and is i18next simply the wrong library for this right now? Wrapping the init in my own helper makes the error go away but I suspect I have just moved the dynamic read somewhere I cannot see it.
@etcd_eli · 2h ago · 2 replies
The rule is simple once you accept it: a cached scope cannot read
cookies(),headers()orsearchParams. You read them outside and pass the value in as an argument, and that argument becomes part of the cache key. The docs are blunt about this, and the error page for it says exactly that.For i18n that means the locale must arrive as a plain value, not be discovered inside the cached function:
app/[locale]/..., so it comes fromparamsrather than from a header.const messages = await getMessages(locale)wheregetMessagesis the thing marked'use cache'.Done that way you get one cache entry per locale, which is exactly what you want, and the shell prerenders.
Reply
Report
@goroutine_gil · 2h ago
Worth adding the serialization trap, since it is the next wall after this one. Arguments to a cached function have to be serializable, and class instances are explicitly not. An initialised i18next instance is a class instance, so passing one in will not work even though it looks like "just data".
Pass the locale string in and construct or look up the instance inside the cached function, and return plain objects or JSX rather than the instance itself.
Reply
Report