new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Next.js · Guides · all subjects

building/cache-components

85 notes in this subject, read out of this brain and free to use. This is page 1 of 2.

Cache Components effect on route rendering

Cache Components adds ◐ Partial Prerender and makes it the default rendering model. Each route is split into a static shell prerendered at build time and dynamic parts that stream in at request time. Routes sit on a spectrum from fully static (○) to partially prerendered (◐) and are no longer fully server-rendered. A route shows ƒ only when it has nothing to prerender, such as Route Handlers that depend on the request, Proxy (Middleware), and dynamic metadata like icon or opengraph-image.

Soft tags passed to cache handler get() method as softTags parameter

In the cache handler API, soft tags are passed to the get() method as the softTags parameter. The handler should check whether any soft tag has been invalidated after the cache entry's timestamp.

Cache handler getExpiration() returns most recent revalidation timestamp

The getExpiration() method in the cache handler API returns the most recent revalidation timestamp across all provided tags, or 0 if none have been revalidated. It can also return Infinity to signal that the soft tags should instead be passed to get() and checked for expiration there. A handler should treat an entry as stale if the returned timestamp is newer than the entry's own timestamp.

revalidatePath invalidates leaf route tag and ancestor layout soft tags

When revalidatePath('/blog/hello') is called, it invalidates cache entries associated with that path's leaf route tag and its ancestor layout soft tags. For example, it invalidates _N_T_/layout, _N_T_/blog/layout, _N_T_/blog/hello/layout, and _N_T_/blog/hello.

Explicit tags set by developer using cacheTag() or fetch next option

Explicit tags are set by the developer using cacheTag() inside a use cache function, or via next: { tags: [...] } on a fetch call. When revalidateTag('my-tag', 'max') is called, all cache entries with that tag are invalidated.

Soft tags automatically generated from route path with _N_T_ prefix

Soft tags are automatically generated by Next.js based on the route path, prefixed with _N_T_. For example, the route /blog/hello generates soft tags like _N_T_/layout, _N_T_/blog/layout, _N_T_/blog/hello/layout, and _N_T_/blog/hello. Each segment in the path gets a layout tag, plus the leaf route itself.

Use cache directive in data functions for App Shell inclusion

Data helpers should use 'use cache' at the module level so all exported functions are cached. This allows their results to be included in the static App Shell. When accessing runtime APIs like cookies or headers, wrap them in Suspense boundaries so their fallback UI is included in the static shell instead.

Derive dialog state from search params for initialization effects

When a dialog runs initialization logic (like focusing an input) each time it opens, Activity's state preservation can prevent effects from re-running. To fix this, derive the dialog state from something outside the preserved component state like a search parameter. This way, when navigating away and returning, the search param is cleared, so the dialog state becomes false and the initialization effect runs again when opening the dialog.

Reset form state immediately on submit

After submitting a form with `router.push`, reset state in the event handler to keep the form fresh. Since Activity preserves the page, navigating back would otherwise show the previous input values still in the form.

Use useLayoutEffect cleanup to reset stale status messages

For status messages set after form submission, use a `useLayoutEffect` cleanup function to reset the form and status state when Activity hides the component. Track submission state with a ref to ensure cleanup only runs after successful submission, preserving drafts if the user navigates away mid-draft without submitting.

Form.reset() via callback ref for Activity

Use a callback ref to call `form.reset()` when Activity hides the component. This resets all form fields whenever the user navigates away.

Activity preserves state across authentication changes

Activity preserves local component state (`useState`, DOM input values) across navigations, including authentication changes. This is standard React behavior: props changing (such as receiving a new user) triggers a re-render but does not reset existing state. A draft composed by one user shouldn't be visible to another.

Use window.location.href for logout to clear client state

For logout flows, use `window.location.href` instead of `router.push` to trigger a full page reload, which clears all client-side state.

Disable page-level styles when hidden by Activity

Page-level styles (CSS variables, z-index, global classes) can affect visible pages when the originating component is hidden by Activity. Use a callback ref to toggle the stylesheet's `media` attribute: set `media=''` when visible and `media='not all'` when hidden.

useLayoutEffect for managing multiple style elements

Use `useLayoutEffect` when managing multiple style elements or more complex cleanup. On mount, set `styleRef.current.media = ''` to enable. On cleanup, set `styleRef.current.media = 'not all'` to disable when Activity hides the component.

:has selector performance considerations with Activity

For global state, prefer a `data-*` attribute that React owns instead of `:root:has(...)` rules. Broad `:has()` selectors are a real performance bottleneck. Reserve `:has()` for local parent/child styling within a component. If the hidden component defines the global `:has` rule, toggling the stylesheet disables it.

Activity component direct usage

Cache Components uses Activity automatically at the route level, but you can also use `<Activity>` directly in your own components with `<Activity mode={expanded ? 'visible' : 'hidden'}>`. This is useful for tabs, expandable panels, or any UI where you want to hide content without unmounting it.

Prerender hidden content with Activity and Suspense

Activity can prerender content the user hasn't seen yet. Hidden boundaries render at lower priority. A Server Component can start fetching data immediately and pass the promise to a client component. The client component uses Activity to hide the content until the user requests it, and `use()` to resolve the promise when rendering. This lets you prefetch data for content the user is likely to view next.

Effect cleanup runs on Activity hide

When Activity hides content, React runs effect cleanup functions just like it does on unmount. This means timers, subscriptions, and media playback pause automatically if you have proper cleanup in `useEffect`.

Pause media playback on Activity hide

For media elements like `<video>` and `<audio>`, `display: none` does not stop playback. Add explicit cleanup with `useLayoutEffect` to pause the media when Activity hides the component. When the component becomes visible again, the playback position is preserved since the DOM node was never removed.

Distinguish first mount from Activity re-show

Effects run on every hide-to-visible transition, not just the initial mount. To distinguish the first mount from subsequent visibility changes, use a ref that persists across hide/show cycles. Refs aren't cleaned up, so after the first mount the ref stays true, and subsequent Effect runs can detect re-visibility.

Reset specific state on user change without full reload

To reset specific state when the user changes without a full reload, use a `useEffect` that compares the current user ID to the previous one via a ref. When they differ, reset the state. Alternatively, key components by user ID to let React handle the reset automatically.

Cache Components and Activity enabled by default

Cache Components must be enabled by setting `cacheComponents: true` in the next.config.js file. When enabled, Next.js uses React's `<Activity>` component to preserve UI state across navigations.

Activity preserves pages instead of unmounting

Instead of unmounting pages on navigation, Next.js hides them using React's `<Activity>` component. The Activity component keeps the DOM in the document with `display: none`, preserving both React state and DOM state including form drafts, scroll positions, expanded `<details>` elements, and video playback progress.

Activity preserves up to 3 routes

Next.js preserves up to 3 routes using Activity. Beyond that, the oldest route is evicted and will re-render fresh.

useRouter().bfcacheId for resetting state with React key

Use `useRouter().bfcacheId` as a React `key` to reset component subtrees. A single `<Fragment key={bfcacheId}>` resets an entire subtree on push or replace navigations (including `<Link>` clicks and `router.push` / `router.replace`) while still restoring state on browser back/forward. This is mainly a migration tool; for new code, prefer per-pattern resets.

Reset transient dropdown state in useLayoutEffect cleanup

To reset transient open/closed state for dropdowns and popovers, close them in a `useLayoutEffect` cleanup function. When Activity hides the component, the cleanup function runs and resets the state. Using `useLayoutEffect` ensures the cleanup runs synchronously before the component is hidden, avoiding any flash of stale state.

Link onNavigate callback to close dropdowns

You can use `Link`'s `onNavigate` callback to close dropdowns immediately when a navigation link is clicked.

Cache components with 'use cache' directive

The 'use cache' directive marks a function as cacheable, turning it into a cache component. The first time a cache component runs, whatever it returns will be cached and reused. If a cache component's inputs are available before the request arrives, it can be prerendered just like a static component, allowing the page to remain static.

Cache components with shared data

When a component's data is shared across all users and doesn't change between requests, caching is the right choice to make the component stable and prerenderable. This applies to data like product catalogs that are the same for every user.

Example: cache component with 'use cache'

```tsx async function ProductList() { 'use cache' const products = await db.product.findMany() return <List items={products} /> } ``` A cache component that marks a function as cacheable so the first execution is cached and reused for subsequent requests.

Cache Components work by default with self-hosted Next.js

Cache Components work by default with Next.js and is not a CDN-only feature. This includes deployment as a Node.js server through next start and when used with a Docker container.

Cache Components with TanStack Query data function

When caching data with Cache Components, add 'use cache' to the data function (such as getProject), not around dehydrate(). Caching the dehydrated state also caches TanStack Query metadata such as timestamps, which can serve stale data on later requests.

revalidateTag requires cacheLife profile argument in Next.js 16

revalidateTag now requires a second argument specifying a cacheLife profile. The single-argument form is deprecated and produces a TypeScript error. Example: revalidateTag('posts', 'max'). If you need immediate expiration rather than stale-while-revalidate, use updateTag in Server Actions instead.

updateTag API for read-your-writes semantics in Next.js 16

updateTag is a new Server Actions-only API providing read-your-writes semantics where a user makes a change and the UI immediately shows the change rather than stale data. It expires and immediately refreshes data within the same request. Use it for interactive features like forms and user settings where users expect to see their updates instantly.

refresh function to refresh client router from Server Action

The refresh function allows you to refresh the client router from within a Server Action. Example: import { refresh } from 'next/cache'; export async function markNotificationAsRead(notificationId) { await db.notifications.markAsRead(notificationId); refresh(); }

cacheLife and cacheTag now stable APIs in Next.js 16

cacheLife and cacheTag are now stable in Next.js 16. The unstable_ prefix is no longer needed. Update imports from: import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag } from 'next/cache' to: import { cacheLife, cacheTag } from 'next/cache'

Partial Prerendering (PPR) replaced by cacheComponents in Next.js 16

Next.js 16 removes the experimental Partial Prerendering (PPR) flag and configuration options, including the route level segment 'experimental_ppr'. Starting with Next.js 16, you can opt into PPR using the cacheComponents configuration: { cacheComponents: true }. PPR in Next.js 16 works differently than in Next.js 15 canaries, so if using PPR today, stay in current Next.js 15 canary.

experimental.dynamicIO and experimental.useCache removed in Next.js 16

The experimental.dynamicIO and experimental.useCache flags have been removed in Next.js 16. If actively using these flags, migrate to top-level cacheComponents configuration. If not actively adopting Cache Components, remove the flags instead. Enabling cacheComponents can surface build errors for uncached data outside Suspense and requires adopting the Cache Components model.

Common CSP violations and solutions

Common CSP violations: (1) Inline styles - use CSS-in-JS libraries supporting nonces or move to external files. (2) Dynamic imports - ensure they're allowed in script-src policy. (3) WebAssembly - add 'wasm-unsafe-eval' if using WebAssembly. (4) Service workers - add appropriate policies for service worker scripts.

Nonce in Next.js automatically applied to framework scripts

When a nonce is extracted from the Content-Security-Policy header, Next.js automatically attaches it to: framework scripts (React, Next.js runtime), page-specific JavaScript bundles, inline styles and scripts generated by Next.js, and any Script components using the nonce prop. Manual nonce addition to each tag is not needed.

How nonce is extracted and applied in Next.js

The nonce workflow: (1) Proxy generates a unique nonce for the request, adds it to Content-Security-Policy header, and sets it in custom x-nonce header. (2) During rendering, Next.js parses the Content-Security-Policy header and extracts the nonce using the 'nonce-{value}' pattern. (3) Next.js attaches the nonce to framework scripts, page bundles, inline styles/scripts, and Script components.

Proxy matcher configuration for CSP nonce

Proxy can be filtered to run on specific paths using a matcher configuration. It is recommended to ignore matching prefetches from next/link and static assets. Example matcher excludes: api routes, _next/static, _next/image, and favicon.ico, plus requests with header 'next-router-prefetch' or header 'purpose' with value 'prefetch'.

Nonce generation and CSP header setup with Proxy

Proxy example showing nonce setup: ```ts import { NextRequest, NextResponse } from 'next/server' export function proxy(request: NextRequest) { const nonce = Buffer.from(crypto.randomUUID()).toString('base64') const isDev = process.env.NODE_ENV === 'development' const cspHeader = ` default-src 'self'; script-src 'self' 'nonce-${nonce}' 'strict-dynamic'${isDev ? " 'unsafe-eval'" : ''}; style-src 'self' 'nonce-${nonce}'; img-src 'self' blob: data:; font-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests; ` const contentSecurityPolicyHeaderValue = cspHeader.replace(/\s{2,}/g, ' ').trim() const requestHeaders = new Headers(request.headers) requestHeaders.set('x-nonce', nonce) requestHeaders.set('Content-Security-Policy', contentSecurityPolicyHeaderValue) const response = NextResponse.next({ request: { headers: requestHeaders } }) response.headers.set('Content-Security-Policy', contentSecurityPolicyHeaderValue) return response } ```

Reading nonce in App Router with headers function

In Next.js App Router, read the nonce from a Server Component using the headers function: ```tsx import { headers } from 'next/headers' import Script from 'next/script' export default async function Page() { const nonce = (await headers()).get('x-nonce') return ( <Script src="https://www.googletagmanager.com/gtag/js" strategy="afterInteractive" nonce={nonce} /> ) } ```

CSP without nonces in next.config.js

For applications not requiring nonces, set CSP header directly in next.config.js: ```js const isDev = process.env.NODE_ENV === 'development' const cspHeader = ` default-src 'self'; script-src 'self' 'unsafe-inline'${isDev ? " 'unsafe-eval'" : ''}; style-src 'self' 'unsafe-inline'; img-src 'self' blob: data:; font-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests; ` module.exports = { async headers() { return [ { source: '/(.*)', headers: [ { key: 'Content-Security-Policy', value: cspHeader.replace(/\n/g, ''), }, ], }, ] }, } ```

Subresource Integrity (SRI) for hash-based CSP

Next.js offers experimental support for hash-based CSP using Subresource Integrity (SRI) as an alternative to nonces. SRI generates cryptographic hashes of JavaScript files at build time and adds them as integrity attributes to script tags, allowing browsers to verify files haven't been modified. This enables static generation while maintaining strict CSP.

Enabling SRI in next.config.js

Add experimental SRI configuration to next.config.js: ```js const nextConfig = { experimental: { sri: { algorithm: 'sha256', // or 'sha384' or 'sha512' }, }, } module.exports = nextConfig ```

Benefits of SRI over nonces

SRI advantages over nonces: pages can be statically generated and cached, static pages work with CDN caching, no server-side rendering required per request for better performance, and hashes are generated at build time ensuring integrity.

SRI limitations

Subresource Integrity limitations: the feature is experimental and may change or be removed, it is only supported in App Router (not Pages Router), and hashes are build-time only so it cannot handle dynamically generated scripts.

CSP with SRI configuration example

CSP configuration with SRI enabled: ```js const isDev = process.env.NODE_ENV === 'development' const cspHeader = ` default-src 'self'; script-src 'self'${isDev ? " 'unsafe-eval'" : ''}; style-src 'self'; img-src 'self' blob: data:; font-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests; ` module.exports = { experimental: { sri: { algorithm: 'sha256' } }, async headers() { return [ { source: '/(.*)', headers: [ { key: 'Content-Security-Policy', value: cspHeader.replace(/\n/g, ''), }, ], }, ] }, } ```

Third-party scripts with CSP in App Router

When using third-party scripts with CSP in App Router: ```tsx import { GoogleTagManager } from '@next/third-parties/google' import { headers } from 'next/headers' export default async function RootLayout({ children }: { children: React.ReactNode }) { const nonce = (await headers()).get('x-nonce') return ( <html lang="en"> <body> {children} <GoogleTagManager gtmId="GTM-XYZ" nonce={nonce} /> </body> </html> ) } ```

Third-party scripts CSP policy domains

When allowing third-party scripts with CSP, update the CSP header to include necessary domains: ```ts const cspHeader = ` default-src 'self'; script-src 'self' 'nonce-${nonce}' 'strict-dynamic' https://www.googletagmanager.com; connect-src 'self' https://www.google-analytics.com; img-src 'self' data: https://www.google-analytics.com; ` ```

What is a nonce in CSP

A nonce is a unique, random string of characters created for one-time use. It is used with CSP to selectively allow certain inline scripts or styles to execute, bypassing strict CSP directives. The nonce must be unpredictable and unique for every request - if an attacker wanted to load a script, they would need to guess the nonce value.

Cache Components opt-out codemod (16.3)

The cache-components-instant-false codemod adds `export const instant = false` to every page, layout, and default file in the app directory that doesn't already export instant. Run with `npx @next/codemod@canary cache-components-instant-false ./app`. This allows enabling cacheComponents globally and then removing opt-outs route by route. It skips Client Components with 'use client' and files already declaring instant. For src/ projects, use ./src/app as the path.

ViewTransition component basics

React's ViewTransition component integrates with the browser's View Transitions API to handle animations declaratively. You name the elements that should persist using the name prop, and the browser animates between their old and new positions. Import with: import { ViewTransition } from 'react'. ViewTransition animations are activated by React Transitions, Suspense, and useDeferredValue. Regular setState calls do not trigger them. In Next.js, route navigations are transitions, so ViewTransition animations activate automatically during navigation.

Browser support for View Transitions API

React's View Transitions integration uses newer API features (transition types and view-transition-class) available in Chromium 125+ and recent Safari and Firefox versions. Some animations may behave differently in Safari. Without browser support, the application works normally; the transitions do not animate.

Shared element morphing pattern

Wrap both the old and new element in ViewTransition components with the same name prop to create a shared element morph. React finds elements with the same name on the old and new pages, then animates between their size and position automatically. No additional props are needed for the morph to work. The morph plays when the destination content renders in the same commit as the navigation, which happens with prefetched (cached) pages. If the destination suspends into a fallback first, no pair forms and the content animates with its enter animation instead when it arrives.

ViewTransition share and default props for morphing

To customize morph animation, add share="morph" together with default="none". The share prop assigns the morph class to the view transition, which you can target with CSS pseudo-elements (::view-transition-group and ::view-transition-image-pair). The default="none" prop keeps each named ViewTransition from running its own crossfade on every unrelated transition. Without it, every named ViewTransition animates whenever any transition runs on the page. When you add default="none" to a named pair, keep the explicit share prop. With default="none" and no share prop, the pair silently stops morphing.

Suspense reveal pattern with ViewTransition

Wrap the Suspense fallback in a ViewTransition with an exit animation (e.g., exit="slide-down"), and the content in a ViewTransition with an enter animation (e.g., enter="slide-up"). Both should have default="none" to prevent animation during unrelated transitions. The exit animation should be fast (recommended 150ms) while the enter animation should be slower (recommended 210ms) with a delay equal to the exit duration, allowing the old content to leave before the new content becomes visible.

Give your agent this brain