cacheComponents and theming #93689
Replies: 4 comments
-
|
The pattern you're looking for is to read the cookie/header outside the cached scope, then pass it as an argument to the cached component. Next.js uses the arguments as part of the cache key, so each theme variant gets its own cache entry — shared across all users with that theme. This way you get shared server-side cache per theme (not per user), the Suspense handles the runtime boundary cleanly without blocking the static shell, and you avoid If your themes are finite and known upfront, you can also prerender all variants at build time using |
Beta Was this translation helpful? Give feedback.
-
|
For now, you can try with either of these (favoring the first one). Single prerender, client paints themeOne static HTML per page. An inline For server-rendered toggle state (e.g. button
However...
Prerender per known theme
Each route ends up with one cache slot per theme.
But then...
I think you likely just want the first approach. The second one is more for like, logged-in/out "variants". Happy to keep on discussing. |
Beta Was this translation helpful? Give feedback.
-
|
Great question about the Best Practice: Read Cookie Outside, Pass as Prop// app/layout.tsx or middleware
import { cookies } from 'next/headers';
export default async function RootLayout({ children }) {
// Read theme cookie OUTSIDE the cached scope
const cookieStore = await cookies();
const theme = cookieStore.get('theme')?.value || 'light';
return (
<html data-theme={theme}>
<body>
{/* Pass theme as prop to cached components */}
<CachedLayout theme={theme}>{children}</CachedLayout>
</body>
</html>
);
}For Cached Components// components/CachedLayout.tsx
'use cache: private'
export async function CachedLayout({ theme, children }) {
// This component is cached per user (private)
// theme is part of the cache key automatically
return (
<div className={`theme-${theme}`}>
<Header theme={theme} />
{children}
</div>
);
}Alternative: Hybrid Approach// Use 'use cache' for the static shell
// Use 'use cache: private' only for user-specific parts
export async function Page() {
return (
<CachedStaticShell>
<UserSpecificThemeWrapper>
<CachedContent />
</UserSpecificThemeWrapper>
</CachedStaticShell>
);
}Key Insight
This gives you the best of both worlds: static caching for content, per-user caching for theming. |
Beta Was this translation helpful? Give feedback.
-
|
For theming, I would not try to make the whole page vary by cookie/header if the theme is only visual — that tends to make too much of the tree request-specific. Keep the page/static data cacheable. Example: export default async function RootLayout({ children }) { |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Summary
I have recently run into a dilemma regarding cacheComponents. I would like to cache as much of my platform to improve performance. This has lead me to start introducing
use cacheon a lot of pages.Option A
I can use
use cache: private, but doesn't that defeat the purpose of caching to some extent, since my user won't visit that same page multiple times.Option B
I can add a Suspense to my root layout, but I wouldn't that cause the rendering to be postponed until the headers have loaded?
The solution I would like is to prerender depending on known states of a cookie or header. Something similar to how
generateStaticParamswork. Or just another solution for better handling prerendering and caching for theming.Additional information
Example
No response
Beta Was this translation helpful? Give feedback.
All reactions