Session read can't be prerendered into static shell with Cache Components
When Cache Components are enabled, a session read happens at request time and can't be prerendered into the static shell. Authenticated UI must stream in behind a Suspense boundary instead.
Reading cookies outside Suspense boundary is build error with Cache Components
With Cache Components enabled, reading cookies() outside a Suspense boundary is a build error. A component that reads the session must sit behind a Suspense boundary.
Share user across components with useUser hook
Create a UserProvider context that wraps a Promise<User>. Pass the promise through context and unwrap it with use() in Client Components. The Server Component behind the boundary creates the promise without awaiting it, and each consumer resolves it behind its own boundary.
Cache session-derived data with userId parameter
Pass the user id into a plain 'use cache' function to keep results on the server, keyed by the id. The id becomes part of the cache key. Use cacheTag() to invalidate the cache later. An exported function should resolve the user and pass only the id to the cached function to prevent callers from requesting another user's data by passing a different id.
Cache keys and tags stored in plain text
A cached function's arguments and captured variables are serialized into its cache key, and cacheTag values are stored as written. Neither is hashed. Key and tag on stable identifiers like user id, and keep secrets, passwords, tokens, and raw emails out of arguments and tags.
use cache: remote for durable cross-instance cache
Plain 'use cache' keeps entries in memory as best effort and doesn't persist across serverless instances. Use 'use cache: remote' for durable, shared storage when data must survive across instances and requests.
updateTag() refreshes cached entries
When a Server Action changes a user's data, call updateTag() with the same cache tag used by the cached entry to refresh it. Re-read the session inside the action to re-authorize it.
cacheLife default profile and prefetching
A 'use cache: private' scope uses the default profile (five-minute stale) unless set otherwise. Keep stale at 30 seconds or more to stay in prefetching. Below 30 seconds, the scope drops out of prefetching.
Link prefetch={true} for routes with params or searchParams
A route that depends on params or searchParams values needs <Link prefetch={true}> on links pointing at it to opt into per-link prefetching, which resolves per-link data ahead of the click.
Session helper example with iron-session
Example getSession function that reads encrypted session cookie with iron-session: async function getSession() { const cookie = (await cookies()).get(COOKIE_NAME)?.value; if (!cookie) return {}; return unsealData<SessionData>(cookie, { password }) }
getCurrentUser with use cache: private
Example getCurrentUser function using 'use cache: private' that reads session, checks userId, retrieves user from database, and redirects if not authenticated. Directive: 'use cache: private'
Announcements and Dashboard component layout
Example showing Announcements component with 'use cache' that prerenders into static shell, and Dashboard component behind Suspense boundary that reads session and streams in on request.
getNotes cached by userId
Example showing exported getNotes() that resolves the current user, then calls unexported getNotesByUserId(userId) with 'use cache' directive, cacheTag, and cacheLife to cache notes per user on server.
addNote Server Action with session re-verification
Example Server Action that re-reads session for authorization, saves note, then calls updateTag() with the same tag used by cached notes to invalidate the cache.
Instant navigation requires per-session App Shell
A route that reads the session produces a per-session App Shell with authenticated content. This shell is prefetched and cached per session, making navigations to it instant.
Data Access Layer pattern for session reads
Centralize session reads, checks, and return narrow user data in one Data Access Layer function like getCurrentUser(). This prevents different code paths from reading the session multiple times.
Use React use() to unwrap promises in Client Components
In Client Components, use React's use() function to unwrap a Promise<User> passed through context. Because use() suspends, keep the component behind a Suspense boundary.