@zeit/next-typescript (should be removed from next.config.js and .babelrc)
Next.js FAQ & Answers
6000 expert Next.js answers researched from official documentation. Every answer cites authoritative sources you can verify.
Jump to section:
Upgrading > Version-Specific Migration Guides
91 questionsNo, Next.js 16 no longer overrides scroll-behavior: smooth during navigation. To restore previous behavior, add data-scroll-behavior="smooth" to the element.
You can specify: patch, minor, major, NPM dist tags (latest, canary, rc), or exact versions (e.g., 15.0.0). The default is 'minor' for stable versions.
out/about.html (changed from out/about/index.html). Set trailingSlash: true in next.config.js to revert.
18.17 (increased from 16.14 due to end-of-life status of Node.js 16.x)
12.22.0 (increased from 12.0.0, which is the first version with native ES Modules support)
next-async-request-api (adds await or React.use() wrappers to cookies(), headers(), draftMode())
next-lint-to-eslint-cli (creates eslint.config.mjs and updates package.json scripts)
next-request-geo-ip (installs @vercel/functions and replaces geo/ip properties)
Once (changed from twice). Use NODE_ENV === 'development' instead of checking process.argv for 'dev'.
images: { localPatterns: [{ pathname: '/assets/**', search: '?v=1' }] }
@next/codemod or @next-codemod-error (typecasts are prefixed with UnsafeUnwrapped)
cookies(), headers(), draftMode(), params (in layouts, pages, routes, metadata image files), and searchParams (in pages)
NextRequest.geo and NextRequest.ip (these values are now provided by hosting providers)
Explicit default.js files. Builds fail without them. Files should call notFound() or return null.
next-image-experimental (removes layout, objectFit, objectPosition, lazyBoundary, and lazyRoot props)
remove-unstable-prefix (e.g., converts unstable_cacheTag to cacheTag)
Yes, except for the Next.js caches. You can disable this with cleanDistDir: false in next.config.js
next/image was renamed to next/legacy/image, and next/future/image was renamed to next/image
The 'modules' and 'render' options (deprecated in v9.5 and removed in v11)
--dry (execute a dry-run without modifying code) and --print (display changed output for comparison)
Yes, Webpack 5 became the default for all Next.js applications in Next.js 11
TypeScript > Framework Type Definitions
64 questions'next/server'. Import with: import { NextResponse } from 'next/server' and import type { NextRequest } from 'next/server'
Route
{} (empty object). The documentation states req.query is an object containing query string parameters that defaults to {}.
An object with name and value properties. It accepts a cookie name (string) parameter.
Promise. For example: params: Promise<{ slug: string }>. You must use async/await or React's use function to access parameter values.
{} (empty object). The documentation states req.cookies is an object containing cookies from the request that defaults to {}.
Yes. Event handlers (onLoad, onError, onLoadingComplete) require a Client Component.
isEnabled (boolean property), enable() method, and disable() method.
No. You cannot export both the metadata object and generateMetadata function from the same route segment.
LayoutProps. Use it with a route string: LayoutProps<'/dashboard'> to automatically infer typed slots from your directory structure.
Yes. Fetch requests inside generateMetadata are automatically memoized for the same data across generateMetadata, generateStaticParams, Layouts, Pages, and Server Components.
Yes. draftMode() is an async function that returns a promise, requiring async/await or React's use hook.
v22.18.0+ (where process.features.typescript is enabled by default). For versions v22.10.0 to v22.17.x, opt in with NODE_OPTIONS=--experimental-transform-types.
No. In version 14 and earlier, cookies was a synchronous function, though this behavior is deprecated in Next.js 15.
satisfies. The documentation shows using the satisfies keyword for validation with these types.
Request or NextRequest. NextRequest is imported from 'next/server' and is an extension of the Web Request API.
NextConfig. Import it with: import type { NextConfig } from 'next' and use it as: const nextConfig: NextConfig = { /* config options */ }
'next'. Import them with: import type { NextApiRequest, NextApiResponse } from 'next'
src (string or StaticImport) and alt (string) are always required. width and height (numbers) are also required unless using the fill prop or static import.
AppRouterInstance. It can be imported from 'next/dist/shared/lib/app-router-context.shared-runtime'.
router.prefetch(href: string, options?: { onInvalidate?: () => void })
'next'. Import with: import type { MetadataRoute } from 'next' and use as MetadataRoute.Robots or MetadataRoute.Sitemap.
http.ServerResponse. The documentation states NextApiResponse is 'an instance of http.ServerResponse' from Node.js.
v15.0.0-RC. Dynamic route parameters are now promises and must be awaited: const { team } = await params
Yes. The metadata object and generateMetadata function exports are only supported in Server Components.
A function receiving { src, width, quality } that returns a string URL.
Use a generic type parameter: NextApiResponse<ResponseData> where ResponseData is your custom type defining the response structure.
next-env.d.ts. This file is auto-generated in the project root and references Next.js type definitions.
PageProps. Use it with a route string literal: PageProps<'/blog/[slug]'> to get autocomplete and strict keys for params.
string. For example, for the route /dashboard/invoices, usePathname would return '/dashboard/invoices'.
Use InferGetServerSidePropsType
By default, permanentRedirect uses push in Server Actions and replace everywhere else.
'next/navigation'. The redirect() function accepts an optional type parameter using RedirectType.replace or RedirectType.push.
next.config.mts. This explicitly indicates it's an ESM module where top-level await and dynamic import are supported.
http.IncomingMessage. The documentation states NextApiRequest is 'an instance of http.IncomingMessage' from Node.js.
Use InferGetStaticPropsType
".next/types/**/*.ts" must be included in the tsconfig.json include array.
React.ReactNode. The type definition is: { children: React.ReactNode }
"empty", "blur", or a data URL string starting with "data:image/..."
Boolean. It determines if a cookie exists by accepting a cookie name (string) parameter.
'next'. Import with: import type { Metadata, ResolvingMetadata } from 'next'
'next/navigation'. Import with: import { useRouter, usePathname, useSearchParams } from 'next/navigation'
RouteContext. Use it with a route string: RouteContext<'/users/[id]'> to get strongly-typed parameters.
next dev, next build, and next typegen all regenerate the next-env.d.ts file.
ResolvingMetadata. This represents a promise resolving to parent segment metadata, enabling child segments to extend rather than replace parent configurations.
Caching and Revalidating > Cache Configuration
64 questionsThe default value for fetchCache is 'auto', which caches requests before Dynamic APIs are used and doesn't cache after.
revalidatePath can be called in Server Functions and Route Handlers but cannot be called in Client Components or Proxy, as it only works in server environments.
The function signature is: revalidateTag(tag: string, profile: string | { expire?: number }): void. It accepts a tag identifier and a profile specifying revalidation behavior, returning nothing.
Next.js looks for a matching request in its Data Cache. If the match is fresh, it will be returned from the cache. If there is no match or the match is stale, Next.js will fetch the resource from the remote server and update the cache with the downloaded resource.
dynamic: 'force-dynamic' forces dynamic rendering, which results in routes being rendered for each user at request time. It is equivalent to setting every fetch() request in a layout or page to { cache: 'no-store', next: { revalidate: 0 } }.
In Next.js 16, the cacheComponents configuration (a top-level config option, not an experimental flag) replaced the experimental dynamicIO flag from Next.js 15.
The minimum value for the revalidate option is 1 second. Setting it to zero or less results in an error. A value of 0 means to revalidate after every request, which implies stale data cannot be tolerated.
next.revalidate: false caches the resource indefinitely, which is equivalent to infinite duration.
With profile='max', data is marked as stale, and the next time a resource with that tag is visited, it will use stale-while-revalidate semantics, serving cached content while fetching fresh data in the background.
The possible values are: 'auto' (default), 'force-dynamic', 'error', and 'force-static'.
If an individual fetch request sets a revalidate number lower than the default revalidate of a route, the whole route revalidation interval will be decreased.
Yes, revalidating the Data Cache will in turn invalidate the Router Cache by re-rendering components.
The default staleTime for Page segments changed to 0 in Next.js 15, meaning the client will always reflect the latest data from the Page component(s) during navigation.
No, invalidating the Full Route Cache does not affect the Data Cache. This allows hybrid cached/uncached data patterns.
When 'use cache' is used at file level, all function exports must be async functions.
The 'max' profile has: stale = 5 minutes, revalidate = 30 days, expire = 1 year.
The maximum character length for the path parameter in revalidatePath is 1024 characters.
Yes, the revalidate value needs to be statically analyzable. For example, revalidate = 600 is valid, but revalidate = 60 * 10 is not.
revalidateTag can only be called in Server Functions and Route Handlers. It cannot be invoked in Client Components or Proxy, as it only works in server environments.
The 'weeks' profile has: stale = 5 minutes, revalidate = 1 week, expire = 30 days.
Unsupported types include class instances, functions, symbols, and URL instances.
The default value for staleTimes.dynamic is 0 seconds (not cached). This default changed from 30 seconds to 0 seconds in Next.js 15.0.0.
The three properties are: stale (how long the client can use cached data without checking the server), revalidate (after this time, the next request will trigger a background refresh), and expire (maximum time before the server must regenerate cached content).
fetchCache: 'only-cache' requires all requests to be cached and errors if any fetch uses 'no-store'.
For self-hosted Next.js applications, the default cache size limit is 50 MB (using an LRU cache), which can be increased using cacheMaxMemorySize config.
The default value for revalidate is false, which caches indefinitely unless individual requests override it. This is semantically equivalent to revalidate: Infinity.
With default prefetching (prefetch={null}), dynamic pages are not cached (0 seconds).
The maximum item size for Vercel's Data Cache is 1 MB per item during the beta period.
The 'hours' profile has: stale = 5 minutes, revalidate = 1 hour, expire = 1 day.
Conflicting options such as { revalidate: 3600, cache: 'no-store' } are not allowed, and both will be ignored. In development mode, a warning will be printed to the terminal.
The lowest revalidate value across route segments determines the entire route frequency.
generateStaticParams must always return an array, even if it's empty.
In Next.js 15, fetch requests use the no-store strategy by default, meaning they won't cache by default. This is a breaking change from Next.js 14.
fetchCache: 'only-no-store' prevents caching and errors if any fetch uses 'force-cache'.
The 'minutes' profile has: stale = 5 minutes, revalidate = 1 minute, expire = 1 hour.
Calling revalidateTag(tag) without a second argument causes immediate expiration requiring blocking revalidation on next request. This pattern is deprecated and may stop working in future versions.
The 'days' profile has: stale = 5 minutes, revalidate = 1 day, expire = 1 week.
With full prefetching (prefetch={true}), both static and dynamic pages are cached for 5 minutes.
During next build, generateStaticParams runs before the corresponding Layouts or Pages are generated.
No, accessing dynamic data sources such as headers or cookies inside a cache scope is not supported. You must pass dynamic data as function arguments instead.
The expire value must be greater than or equal to the revalidate value when both are specified.
The 'seconds' profile has: stale = 30 seconds, revalidate = 1 second, expire = 1 minute.
Setting revalidate: 0 ensures a layout or page is always dynamically rendered even if no Dynamic APIs or uncached data fetches are discovered. It changes the default of fetch requests that do not set a cache option to 'no-store'.
No, cached functions and components cannot directly access runtime APIs like cookies(), headers(), or searchParams. Dynamic data must be passed as function arguments instead.
dynamic: 'force-static' forces static rendering and returns empty values for cookies(), headers(), and useSearchParams().
When you call revalidation functions from a Server Action (revalidateTag, revalidatePath, updateTag, or refresh), the entire client cache is immediately cleared, bypassing the stale time.
You can disable default in-memory caching by setting cacheMaxMemorySize: 0 in your next.config.js file.
With default prefetching (prefetch={null}), static pages are cached for 5 minutes.
No, during incremental static regeneration (ISR), generateStaticParams won't run again.
The possible values are: 'auto' (default), 'default-cache', 'only-cache', 'force-cache', 'default-no-store', 'only-no-store', and 'force-no-store'.
You must enable cacheComponents: true in your next.config.ts/js file to use the 'use cache' directive.
'use cache' is enabled with the Cache Components feature in Next.js v16.0.0.
When dynamicParams: false, dynamic segments not in generateStaticParams will return 404 instead of being generated on-demand.
In Next.js 14, fetch requests use the force-cache strategy by default, meaning data is cached by default.
'use cache' was introduced as an experimental feature in Next.js v15.0.0.
dynamic: 'error' forces static rendering with errors if Dynamic APIs are used.
Caching and Revalidating > Caching APIs and Functions
62 questionsIn Server Functions, it updates the UI immediately if viewing the affected path. In Route Handlers, it marks the path for revalidation on the next visit to the specified path.
Setting revalidate: false caches the resource indefinitely, which is semantically equivalent to revalidate: Infinity.
The type parameter is required when the path contains dynamic segments (e.g., /product/[slug]); omit it for specific URLs (e.g., /product/123).
Request memoization only applies to the GET method in fetch requests. POST and DELETE requests are not memoized.
updateTag can only be called from within Server Actions. It cannot be used in Route Handlers, Client Components, or other contexts.
revalidateTag(tag: string, profile: string | { expire?: number }): void
The 'weeks' profile has: stale: 5 min, revalidate: 1 week, expire: 30 days.
The 'hours' profile has: stale: 5 min, revalidate: 1 hour, expire: 1 day.
The three properties are: stale (client-side cache duration before server revalidation check), revalidate (background refresh interval), and expire (maximum cache lifetime before forced regeneration).
No, you cannot combine 'only-cache' with 'only-no-store' in a single route.
The cache lasts the lifetime of a server request until the React component tree has finished rendering. The memory is reset after rendering completes.
The minimum value for the stale property is 30 seconds, enforced for prefetch usability.
No, the single-argument form is deprecated. It immediately expires the tag entry, causing blocking cache misses on the next request.
The 'seconds' profile has: stale: 30 sec, revalidate: 1 sec, expire: 1 min.
The recommended profile value is 'max', which marks the tag as stale and uses stale-while-revalidate semantics on the next visit.
Tags are idempotent—applying the same tag repeatedly has no additional effect.
The 'minutes' profile has: stale: 5 min, revalidate: 1 min, expire: 1 hour.
No, the revalidate option is unavailable when using runtime = 'edge'.
The 'days' profile has: stale: 5 min, revalidate: 1 day, expire: 1 week.
It forces static rendering and causes an error if components use Dynamic APIs or uncached data.
The 'use cache' directive can be applied at file level (top of file before exports), component level (inside async React components), or function level (inside async functions).
Setting revalidate: 0 opts out of caching entirely, preventing the resource from being cached.
The function returns a void Promise. It is not meant to be consumed.
The allowed values are: false (default - caches indefinitely), 0 (ensures always dynamic), or a number in seconds for the revalidation interval.
Setting revalidate = 0 ensures the route is always dynamic and changes the default fetch behavior to 'no-store'.
Passing false (or omitting the revalidate option) caches data indefinitely or until matching revalidateTag() or revalidatePath() methods are called.
The 'max' profile has: stale: 5 min, revalidate: 30 days, expire: 1 year.
unstable_noStore was deprecated in Next.js v15.0.0 in favor of the connection API.
The defaults are: stale: 5 minutes (client), revalidate: 15 minutes (server), and expire: never by time.
The connection() function replaced unstable_noStore to better align with the future of Next.js.
In Next.js 15, fetch requests are not cached by default (equivalent to cache: 'no-store'). You must explicitly set cache: 'force-cache' to cache individual requests.
The 'default' profile has: stale: 5 min, revalidate: 15 min, expire: 1 year.
cacheTag must be called within a function or component marked with the 'use cache' directive.
The maximum length is 1024 characters, and paths are case-sensitive.
The revalidate value must be statically analyzable. For example, revalidate = 600 is valid, but revalidate = 60 * 10 is invalid.
The maximum is 128 tags per cache entry, with each tag limited to 256 characters.
When used inside unstable_cache, it defers to the cache configuration rather than opting out of static generation.
The three cache options are: 'auto' (default - caches during build for static routes, fetches on every request in dev), 'no-store' (fetches from remote server on every request), and 'force-cache' (returns fresh cached matches, fetches and updates cache if missing or stale).
The allowed values are: 'auto' (default), 'force-dynamic', 'error', and 'force-static'.
updateTag immediately expires the cached data and the next request waits for fresh data, while revalidateTag (with profile='max') can serve stale content while revalidating.
revalidatePath can be called in Server Functions and Route Handlers, but cannot be used in Client Components or Proxy environments.
The allowed values are: 'auto' (default), 'default-cache', 'only-cache', 'force-cache', 'force-no-store', 'default-no-store', and 'only-no-store'.
It forces dynamic rendering for each user at request time and is equivalent to setting all fetches to { cache: 'no-store', next: { revalidate: 0 } }.
unstable_noStore is functionally equivalent to setting cache: 'no-store' on fetch requests.
unstable_cache returns a function that when invoked, returns a Promise that resolves to the cached data.
When used at file level, all function exports must be async functions.
Use React's cache() function for non-fetch scenarios such as database clients, CMS clients, or GraphQL clients that lack inherent memoization.
updateTag is designed for read-your-own-writes scenarios, where a user makes a change and the UI should immediately show the fresh data rather than stale cached data.
The maximum number of tag items is 128 tags total per fetch request.
revalidateTag can be called in Server Functions and Route Handlers, but cannot be used in Client Components or Proxy environments.
It applies in generateMetadata, generateStaticParams, Layouts, Pages, and Server Components. It does not apply to Route Handlers (outside React component tree).
Unsupported types include: class instances, functions (except pass-through), symbols, WeakMaps, WeakSets, and URL instances.
Turbopack
56 questionsYes, experimental.turbopackFileSystemCacheForDev is enabled by default as of v16.1.0.
Customizing resolveExtensions completely overwrites the original resolve extensions, so you must include the default extensions in your custom configuration.
No, custom Sass functions defined in sassOptions.functions are not supported because Turbopack's Rust-based architecture cannot directly execute JavaScript functions.
Yes, Emotion is supported. Next.js is working to port @emotion/babel-plugin to the Next.js Compiler for Turbopack.
Yes, Turbopack is the default bundler for all Next.js projects starting with version 16.0.0. No configuration is needed to use it.
Turbopack moved to beta status with Next.js 13.4, released on May 4, 2023.
Function-level caching where Turbo Engine remembers the input and output of specific marked functions, storing them in an in-memory cache.
Only loaders that return JavaScript code are supported. Loaders transforming stylesheets or images are not currently supported.
The top 300 npm packages used in Next.js applications can compile with Turbopack.
96.3% faster code updates with Fast Refresh on large Next.js applications like vercel.com.
Run next dev --webpack for development and next build --webpack for production builds.
No, instead of invalidating entire files, Turbopack tracks which specific functions and exports are used where. Changing one function doesn't invalidate the entire file.
No, experimental.turbopackFileSystemCacheForBuild is not enabled by default and remains experimental.
No, only the function variants :local() and :global() are supported. Standalone :local and :global pseudo selectors are not supported.
No, .babelrc won't auto-transform code in Turbopack prior to v16.0.0. You must use babel-loader explicitly. Starting with v16.0.0, Babel is automatically enabled if a babel configuration file exists.
When linking dependencies outside the project root (via npm link, yarn link, pnpm link), you must set the root option to the parent directory containing both the project and linked packages.
Lightning CSS, a fast CSS bundler and minifier written in Rust (used by default since Next.js 14.2).
99.8% of tests were passing for next dev --turbo in Next.js 14.2, released in April 2024.
The in-memory cache requires rebuilding on server restart, which can take 10+ seconds for large apps (this limitation was present before filesystem caching was added).
76.7% faster local server startup on vercel.com (a large Next.js application).
45.8% faster initial route compilation without caching on large Next.js applications.
No, custom webpack configurations using the webpack() function are not supported.
No, if you want to use composes in a CSS Module, you must change the .css file to a .module.css file. Turbopack treats .css files as global, unlike webpack.
No, Yarn PnP is not supported and is not planned for Turbopack support in Next.js.
The turbopack key in next.config.js (moved from experimental.turbo which was used in versions 13.0.0 to 15.2.x).
Yes, experimental.turbo was used in Next.js versions 13.0.0 to 15.2.x. It was renamed to turbopack in v15.3.0 and will be removed in Next.js 16.
Turbopack bundles in dev, but in an optimized way with lazy bundling that reduces initial compile times and memory usage, only bundling minimum required assets.
Yes, Turbopack uses a single, unified graph for all environments (server and client), allowing it to handle both in a single compilation pass.
Turbopack auto-detects the project root via lock files: pnpm-lock.yaml, package-lock.json, or yarn.lock.
No, Turbopack always uses Lightning CSS. The useLightningcss config option has no effect on Turbopack.
next build --turbopack enables Turbopack for production builds (alpha status in 15.3, stable in 16.0.0).
Turbopack development mode (next dev --turbo) became stable on October 21, 2024, with Next.js 15.0.0.
Yes, Turbopack supports React Server Components natively for the App Router.
No, StyleX is not currently supported. It requires a Babel transform and support for data: attributes. The team plans to investigate support after next build --turbo is stable.
babel-loader, @svgr/webpack, svg-inline-loader, yaml-loader, string-replace-loader, raw-loader, and sass-loader.
Yes, Turbopack can parallelize work across all available processor cores.
Yes, Turbopack supports importing Sass files including node_modules Sass files out of the box.
No, Fast Refresh works automatically with Turbopack without any configuration needed.
Options must be plain JavaScript primitives, objects, or arrays. You cannot pass require() plugin modules as option values.
No, Turbopack does not support webpack plugins. Only webpack loaders are supported.
Upgrading > Feature Deprecations & Removals
56 questionsYes, all parallel route slots now require explicit default.js files. Builds will fail without them.
Yes, serverRuntimeConfig and publicRuntimeConfig were removed in Next.js 16. Use environment variables instead.
Removes @next/font package and converts imports to built-in next/font (Next.js 13.2).
cacheComponents: true. The experimental.ppr flag and configuration options were removed.
Yes, the supported browsers were changed to drop Internet Explorer and target modern browsers in Next.js 13.
300 seconds (5 minutes) for statically generated pages or when prefetch={true} is set.
14400 seconds (4 hours), changed from 60 seconds in previous versions.
Yes, local images with query strings require explicit images.localPatterns.search configuration to prevent enumeration attacks.
Yes, webpack 4 support was eliminated in Next.js 12. Applications must upgrade to webpack 5.
Eliminates the experimental_ppr Route Segment Config from App Router pages and layouts (Next.js 16).
Renames next/image to next/legacy/image and next/future/image to next/image (Next.js 13).
Yes, middleware.ts is deprecated and will be removed in a future version. Use the proxy convention (proxy.ts) instead.
Yes, unstable_rootParams was removed in Next.js 16. An alternative API is planned for a future minor release.
Yes, Turbopack is now stable and the default in Next.js 16. The experimental.turbopack configuration moved to top-level turbopack option.
Moves ImageResponse imports from next/server to next/og (Next.js 14).
Yes, support for experimental-edge runtime value was removed in Next.js 15. Use edge instead.
Yes, the target configuration option was deprecated in Next.js 12 (and later removed in Next.js 13).
Yes, useFormState is deprecated in favor of useActionState in React 19, which Next.js 15 supports.
Node.js 16.14.0 (bumped from 12.22.0, as versions 12.x and 14.x reached end-of-life).
No, the component no longer requires manually adding an tag as a child in Next.js 13. It always renders automatically.
Transitions projects from next lint to ESLint CLI, creating an eslint.config.mjs file and updating package scripts (Next.js 16).
Yes, the deprecated target option of next.config.js was removed in Next.js 13.
The appIsrStatus, buildActivity, and buildActivityPosition options were removed from devIndicators in Next.js 16.
No, Next.js 16 no longer overrides smooth scroll behavior during navigation by default. Use data-scroll-behavior="smooth" on the element to restore prior behavior.
Yes, next/legacy/image is deprecated in Next.js 16. Users should migrate to next/image.
Node.js 12.22.0 (bumped from 12.0.0, as this is the first version with native ES Modules support).
Installs @vercel/functions and replaces NextRequest properties with corresponding utility functions (Next.js 15).
Yes, the @next/font package was fully removed in Next.js 14 in favor of built-in next/font.
[75]. The quality prop is now restricted to only 75 by default and requires explicit configuration for other qualities.
Yes, images.domains is deprecated in Next.js 16. Use images.remotePatterns instead.
Yes, auto-instrumentation for Speed Insights was removed in Next.js 15, requiring manual setup.
Yes, the @next/font package was removed in Next.js 15. Use the built-in next/font instead.
The next/image import was renamed to next/legacy/image, while next/future/image became the new next/image.
The value 16 was removed from the default imageSizes array because very few projects serve 16 pixel width images.
Yes, the React Compiler was promoted from experimental to stable in Next.js 16 via the reactCompiler: true configuration.
Yes, the next lint command was removed in Next.js 16. Users should use ESLint or Biome directly instead.
Migrates viewport metadata into a separate viewport export (Next.js 14).
Yes, the geo and ip properties on NextRequest were removed in Next.js 15. Use hosting provider alternatives or @vercel/functions.
No, synchronous access is fully removed in Next.js 16. These APIs must be accessed asynchronously.
No, GET Route Handlers are no longer cached by default in Next.js 15.
Removes the unstable_ prefix from stabilized APIs like unstable_cacheTag (Next.js 16).
Node.js 20.9.0 or higher. Node.js 18 is no longer supported in Next.js 16.
Transforms dynamic APIs (cookies(), headers(), draftMode()) to be properly awaited or wrapped with React.use() (Next.js 15).
Yes, these metadata options were deprecated in Next.js 14 and will be removed in a future major version. Use the separate viewport export instead.
Yes, all AMP APIs and configurations were removed in Next.js 16, including the useAmp hook and amp config options.
Upgrading > API Breaking Changes
54 questionsNo. In Next.js 16, searchParams is a Promise that must be awaited in page.js files.
npx @next/codemod upgrade [revision]. The upgrade command automatically runs codemods and updates Next.js, React, and React DOM.
No. Starting with Next.js 16, synchronous access is fully removed. It can only be accessed asynchronously using await.
14400 seconds (4 hours). It was changed from the previous default of 60 seconds.
Turbopack. Starting with Next.js 16, Turbopack is stable and used by default with next dev and next build.
18.17. The minimum Node.js version was bumped from 16.14 to 18.17, since 16.x has reached end-of-life.
No. Supported Browsers have been changed to drop Internet Explorer and target modern browsers.
No. The next lint command has been removed entirely. Use Biome or ESLint directly.
cacheComponents. The experimental.dynamicIO configuration has been renamed to cacheComponents.
next-async-request-api. Use: npx @next/codemod@latest next-async-request-api . - It adapts cookies(), headers(), and draftMode() from next/headers to asynchronous patterns.
12.22.0. The minimum Node.js version was bumped from 12.0.0 to 12.22.0 which is the first version with native ES Modules support.
No. The @next/font package has been removed. Use the built-in next/font instead.
No. Auto instrumentation for Speed Insights was removed in Next.js 15. Follow the Vercel Speed Insights Quickstart guide to continue using it.
next/image. The previous next/image was renamed to next/legacy/image, and next/future/image was renamed to next/image.
No. Page segments are no longer reused during navigation via or useRouter by default. They are still reused during browser back/forward navigation.
true. The swcMinify configuration property was changed from false to true.
No. By default, Next.js 16 will no longer override your scroll-behavior setting during navigation. Use data-scroll-behavior="smooth" attribute on the element to restore previous behavior.
Yes. Query strings now require explicit images.localPatterns.search configuration to prevent enumeration attacks.
Yes. All parallel route slots now require explicit default.js files; builds will fail without them.
16.14.0. The minimum Node.js version was bumped from 12.22.0 to 16.14.0, since 12.x and 14.x have reached end-of-life.
. The next/image component now wraps the in a instead of
No. A new security restriction blocks local IP optimization by default. You must set images.dangerouslyAllowLocalIP: true to allow it (only for private networks).
No. In Next.js 16, params is a Promise that must be awaited in layout.js, page.js, route.js, default.js, opengraph-image, twitter-image, icon, and apple-icon files.
No. The default caching behavior was inverted - fetch() calls are not cached by default. Previously they were cached by default.
No. The next export command has been removed in favor of output: 'export' config.
No. Starting with Next.js 16, synchronous access is fully removed. It can only be accessed asynchronously using await.
No. The Component no longer requires manually adding an tag as a child. In Next.js 13, always renders .
No. Starting with Next.js 16, synchronous access is fully removed. It can only be accessed asynchronously using await.
0 seconds (not cached). It was changed from the previous default of 30 seconds.
runtime: 'experimental-edge' is no longer supported. You must update to runtime: 'edge'.
next-request-geo-ip. Use: npx @next/codemod@latest next-request-geo-ip . - It replaces geo and ip properties of NextRequest with @vercel/functions equivalents.
No. All AMP APIs, configurations, and next/amp imports have been removed.
proxy. The middleware filename convention is renamed to proxy, and the named export middleware must be renamed to proxy.
No. GET functions are no longer cached by default. Use export const dynamic = 'force-static' to opt-in to caching.
remove-unstable-prefix. Use: npx @next/codemod@latest remove-unstable-prefix . - Example: unstable_cacheTag becomes cacheTag.
Builds will fail. Projects with custom webpack configurations must either migrate to Turbopack, use --turbopack flag explicitly, or opt out with --webpack flag.
No. The geo and ip properties have been removed from NextRequest as these values are provided by your hosting provider.
useActionState. The deprecated useFormState hook is still available but useActionState is the new recommended API.
app-dir-runtime-config-experimental-edge. Use: npx @next/codemod@latest app-dir-runtime-config-experimental-edge . - It transforms Route Segment Config runtime value experimental-edge to edge.
No. The target configuration property has been removed and superseded by Output File Tracing.
next-lint-to-eslint-cli. Use: npx @next/codemod@canary next-lint-to-eslint-cli . - It creates eslint.config.mjs and updates package.json scripts.
[75]. The default was changed from allowing all qualities to only quality 75.
WebSocket. Next.js 12 now uses a WebSocket connection for HMR instead of server-sent events.
No. Both serverRuntimeConfig and publicRuntimeConfig have been removed. Use environment variables instead.
Deploying > Server-Based Deployment
52 questionsNo. PORT cannot be set in .env as booting up the HTTP server happens before any other code is initialized.
A reverse proxy can handle malformed requests, slow connection attacks, payload size limits, rate limiting, and other security concerns.
No. When using standalone output mode, it does not trace custom server files. This mode outputs a separate minimal server.js file instead.
No. The server.js file does not run through the Next.js Compiler or bundling process, so the syntax and source code must be compatible with the current Node.js version.
"dev": "node server.js", "build": "next build", "start": "NODE_ENV=production node server.js"
No. Next.js 15 automatically uses sharp when using next start or standalone output mode without manual installation.
Yes. Starting in Next.js 9.0.4, gzip compression is included by default when using next start or a custom server.
Otherwise, once a keep-alive timeout is reached for a given TCP connection, Node.js will immediately terminate that connection without notifying the downstream proxy, resulting in a proxy error.
Rendering Strategies > Caching and Revalidation Approaches
52 questionsNext.js searches the Data Cache for matching requests. Fresh matches return from cache; otherwise, it fetches from the remote server and updates the cache.
No, accessing dynamic data sources such as headers or cookies inside a cache scope is not supported. Retrieve these outside the cached function and pass them as arguments.
Cache as much as possible without preventing any components from opting into dynamic behavior.
Conflicting options are disallowed and generate development warnings.
A high revalidation time is recommended, for instance, 1 hour instead of 1 second.
The number of seconds after which a page can be regenerated (e.g., revalidate: 10 means at most once every 10 seconds).
Required when path contains dynamic segments (must be 'page' or 'layout'); omit for specific URLs.
Entirely opt-in. All dynamic code in any page, layout, or API route is executed at request time by default.
It automatically uses arguments and stringified function code as part of the cache key.
The tag entry is marked as stale, and the next time a resource with that tag is visited, it will use stale-while-revalidate semantics (serve stale content while fetching fresh in background).
GET and HEAD methods. Other methods like POST and DELETE are not memoized.
true (segments not in generateStaticParams render on demand using Streaming Server Rendering).
On-demand revalidation purges appropriate cache entries from the cache. Time-based revalidation keeps the stale data in the cache until the fresh data is fetched.
No, updateTag is specifically designed for Server Actions and can only be used within them.
The lowest revalidate value across multiple fetch requests on the same route determines the entire route's revalidation interval.
Node.js runtime (default). ISR is only supported when using the Node.js runtime.
If the background revalidation fails, the previous data will be kept unaltered (last successfully generated data continues to be served).
Yes, HMR caching applies to all fetch requests by default, preventing fresh data display between HMR refreshes.
No, during revalidation (ISR), generateStaticParams will not be called again.
Next.js fetches the resource from the remote server on every request in development, but will fetch once during next build if the route is statically prerendered.
Invalidates the Router Cache and makes a new request to the server for the current route.
The layout file at that segment, all nested layouts beneath it, and all pages beneath them.
No, invalidating the Full Route Cache alone doesn't affect the Data Cache.
Forces dynamic rendering by changing fetch defaults to 'no-store', though requests explicitly opting into 'force-cache' remain cached.
Renders for each user at request time, equivalent to setting all fetch() requests to { cache: 'no-store', next: { revalidate: 0 } }.
The next request receives the cached (now stale) page immediately while regeneration occurs in the background. Once the new version generates successfully, it replaces the cached version for subsequent requests.
Caching and Revalidating > Rendering Strategies
51 questionsdynamic = 'force-dynamic' (unnecessary), dynamic = 'force-static' (replace with 'use cache'), revalidate (replace with cacheLife), fetchCache (unnecessary with 'use cache'), and runtime = 'edge' (not supported).
'auto' (default - cache as much as possible), 'force-dynamic' (render per-request), 'error' (force static and error on dynamic APIs), 'force-static' (force static and return empty values from dynamic APIs).
Use cache: 'force-cache' in the fetch options to explicitly enable the Data Cache.
No, accessing dynamic data sources such as headers or cookies inside a cache scope is not supported. Pass dynamic data as arguments instead.
With profile='max', the tag entry is marked as stale and uses stale-while-revalidate semantics (serves stale content while revalidating in background). Without the second argument (deprecated), the tag entry expires immediately causing a blocking revalidate/cache miss.
revalidatePath('/blog/[slug]', 'page') where the second parameter (type) is required when the path contains dynamic segments. Type can be 'page' or 'layout'.
When 'use cache' is used at file level, all function exports must be async functions.
ISR is only supported when using the Node.js runtime (default). It is not supported with Static Export or other runtimes.
No, during revalidation (ISR), generateStaticParams will not be called again.
fetch(url, { next: { revalidate: 3600 } }) where the value is in seconds. For example, 3600 revalidates after 1 hour.
- Request Memoization - deduplicates identical fetch calls within a single render pass, 2) Data Cache - persists data fetches across server requests and deployments, 3) Full Route Cache - stores rendered HTML and RSC payload at build time, 4) Router Cache - client-side in-memory storage of RSC payloads.
const nextConfig = { cacheComponents: true, cacheLife: { profileName: { stale: 300, revalidate: 900, expire: 3600 } } } where values are in seconds.
They return empty values, forcing static rendering while allowing the route to still use revalidate, revalidatePath, or revalidateTag.
Set cacheComponents: true in the Next.js config: const nextConfig: NextConfig = { cacheComponents: true }
It is equivalent to setting every fetch() request in a layout or page to { cache: 'no-store', next: { revalidate: 0 } }.
revalidateTag('posts', 'max') where the second parameter 'max' enables stale-while-revalidate semantics. The single-argument form revalidateTag(tag) is deprecated.
import { connection } from 'next/server' followed by await connection() in an async component. It accepts no parameters and returns Promise
In Next.js 14 and earlier, fetch requests are not cached by default, but Next.js will pre-render routes and cache the output. In Next.js 15, the default is 'no-store', meaning fetches are not cached unless explicitly set.
Maximum time before the server must regenerate cached content. After this duration without traffic, the next request triggers synchronous regeneration. Must be longer than revalidate when both are set.
'auto' (default - fetches on every request in dev, once during build), 'no-store' (fetches on every request), 'force-cache' (checks Data Cache first, fetches if not fresh).
If revalidation fails, the last successfully generated data will continue to be served until the next retry attempt.
cookies(), headers(), searchParams, connection(), draftMode(), and unstable_noStore() all trigger dynamic rendering.
The next visitor receives the cached (stale) version immediately for a fast response, while Next.js triggers regeneration in the background. Once the new version is successfully generated, it replaces the cached version for subsequent visitors.
unstable_cache(fetchData, keyParts, options)() where fetchData is an async function, keyParts is an optional array of identifiers, and options is an optional object with tags (array) and revalidate (number or false) properties.
connection() replaces unstable_noStore to better align with the future of Next.js.
fetch(url, { next: { tags: ['user'] } }) where tags is an array of strings for on-demand cache invalidation.
The Full Route Cache is persistent with no automatic expiration time. It persists indefinitely until data revalidation or redeployment.
Only paths provided by generateStaticParams will be served, and unspecified routes will return 404 or match (in the case of catch-all routes).
Network requests (fetch calls), database queries, async file system operations, accessing runtime APIs (cookies(), headers(), searchParams, params), and non-deterministic operations (Math.random(), Date.now(), crypto.randomUUID()).
generateStaticParams runs before the corresponding Layouts or Pages are generated at build time.
The cache lasts the lifetime of a server request until the React component tree has finished rendering.
No, both can only be called in Server Functions and Route Handlers. They cannot be called in Client Components.
'use cache' directive placed at the top of a file, component function, or async function body.
It caches the resource indefinitely, semantically equivalent to revalidate: Infinity.
'auto' (default), 'default-cache', 'only-cache', 'force-cache', 'default-no-store', 'only-no-store', 'force-no-store'.
All pages are dynamic by default. The system automatically identifies content that can be prerendered into a static HTML shell during build time.
Determines server background refresh frequency. When this period expires, the server serves cached content while regenerating fresh data in the background (similar to ISR).
Any fetch with revalidate = 0 or cache: 'no-store' triggers dynamic rendering.
Analytics
50 questionsThe default percentile is P75, which represents the experience of the fastest 75% of users, excluding the slowest 25%.
When a Hobby plan exceeds the limit, a 3-day grace period starts before Vercel stops capturing events. After stopping collection, there is a 7-day waiting period before collection resumes.
The documentation recommends using navigator.sendBeacon() as the primary method and fetch() with keepalive: true as a fallback.
The debug option is automatically enabled when NODE_ENV is set to 'development' or 'test', otherwise it defaults to disabled.
On the Pro plan, additional events cost $3 per 100,000 events (prorated).
Vercel Web Analytics collects: event timestamp, URL and dynamic path, referrer information, filtered query parameters, geolocation (country, state, city level), device OS and version, browser and version, device type (mobile, desktop, tablet), and Web Analytics script version.
Pro users with Web Analytics Plus can track up to 8 properties per custom event.
Allowed values are string, number, boolean, and null. Nested objects are not supported.
Import { track } from '@vercel/analytics' and call track('EventName') or track('EventName', { property: 'value' }) with optional custom data.
Visitor sessions are automatically discarded after 24 hours and are not stored permanently.
The scriptSrc option allows you to load the Web Analytics script from a different URL than the default one.
The instrumentation-client.js file should be placed in the root of your application or inside a src folder if you're using one.
Next.js provides three custom metrics: Next.js-hydration (time for the page to start and finish hydrating in ms), Next.js-route-change-to-render (time for a page to start rendering after a route change in ms), and Next.js-render (time for a page to finish render after a route change in ms).
In App Router, add the Analytics component in your root layout file (app/layout.js or app/layout.tsx), typically within the
tag.Speed Insights captures up to 6 data points per visit across three phases: page load (TTFB and FCP), user interaction (FID and LCP), and page exit (INP, CLS, and LCP if not previously sent).
The beforeSend configuration option accepts a function that receives a BeforeSendEvent and can modify the event or return null to prevent sending.
Vercel calculates scores using HTTP Archive real-world data, mapping metric values to 0-100 scores based on log-normal distributions derived from actual website performance data.
In Pages Router, add the Analytics component in your _app.js or _app.tsx file alongside the Component prop.
After enabling Web Analytics, new routes are scoped at /_vercel/insights after your next deployment.
Next.js monitors initialization time in development and will log warnings if instrumentation-client.js takes longer than 16ms.
The debug option is auto-enabled when NODE_ENV is available and set to 'development' or 'test'.
The endpoint option allows you to report collected analytics to a different URL than the default.
Import the SpeedInsights component from '@vercel/speed-insights/next'.
The navigationType property can be: 'navigate', 'reload', 'back_forward', or 'prerender'.
The route prop allows you to specify the current dynamic route (such as /products/[id]) to aggregate performance metrics for similar routes. It is automatically detected for Next.js, Nuxt, SvelteKit, and Remix.
The beforeSend option accepts a function to modify or filter out event data before it's sent to Vercel, useful for redacting sensitive data or excluding specific events.
Event names, keys, and values cannot be longer than 255 characters each.
No, Vercel Web Analytics allows you to track website traffic without using any third-party cookies. Users are identified by a hash created from the incoming request.
Next.js reports six Web Vitals metrics: TTFB (Time to First Byte), FCP (First Contentful Paint), LCP (Largest Contentful Paint), FID (First Input Delay), CLS (Cumulative Layout Shift), and INP (Interaction to Next Paint).
The default sampleRate is 1, which means all events are sent to the server (100%).
No, custom metrics still work for Pages Router, but they are not tracked anymore for App Router.
No, Vercel Web Analytics does not store IP addresses. Users are identified by a hash created from the incoming request instead.
Next.js provides the useReportWebVitals hook to manage reporting yourself. It must be used with the 'use client' directive.
sampleRate: 0.5 means 50% of events are sent to the server, reducing costs but potentially decreasing data accuracy.
Forms and Mutations > Data Handling
50 questionsSet the bodySizeLimit option in next.config.js under serverActions, accepting formats like '2mb', '10mb', or numeric bytes values. Example: serverActions: { bodySizeLimit: '2mb' }
The type parameter ('page' | 'layout') is required when the path contains dynamic segments like /product/[slug]. It should be omitted for specific URLs like /product/1.
The method, encType, and target attributes are not supported. Additionally, formMethod, formEncType, and formTarget overrides fall back to native behavior.
The Headers object provides: get(name), has(name), entries(), keys(), values(), and forEach(callback) methods.
No. With profile='max', calling revalidateTag marks tagged data as stale, but invalidation only happens when any page using that tag is next visited. It does not immediately trigger many revalidations at once.
useOptimistic returns an array with two elements: [optimisticState, addOptimistic] where optimisticState is the resulting state and addOptimistic is a function to update the optimistic value.
No. Server Components have read-only access to cookies. Only Server Actions and Route Handlers can set or delete cookies.
Set NEXT_SERVER_ACTIONS_ENCRYPTION_KEY as an AES-GCM encrypted key to enable consistent encryption across instances in multi-server deployments.
Server Actions are designed for server-side mutations. The client currently dispatches and awaits them one at a time (sequentially).
When used in a Server Action, redirect returns a 303 (See Other) HTTP response, which is commonly used for redirecting to a success page as a result of a POST request.
No. Arguments passed via JavaScript's .bind() function are NOT encrypted. This provides an opt-out for performance and is consistent with .bind() behavior on the client.
No. File inputs with string actions submit the filename, not the file object.
Next.js creates encrypted, non-deterministic IDs to allow the client to reference and call Server Actions. These IDs are periodically recalculated between builds for enhanced security.
The default is 307 (Temporary Redirect). Next.js prefers 307/308 over legacy 302/301 because they preserve the original request method (POST stays POST), whereas 302 would convert POST to GET.
redirect should be called outside the try/catch block because it throws a NEXT_REDIRECT error to terminate rendering.
The cookies() function is asynchronous and returns a Promise. You must use async/await or React's use() function to access it.
useFormStatus returns an object with: pending (boolean), data (FormData object or null), method (string: 'get' or 'post'), and action (function reference or null).
Server Actions use the POST method exclusively. Only this HTTP method can invoke them.
The replace prop defaults to false. When true, it replaces the history state instead of pushing a new entry.
The default maximum request body size is 1MB, designed to prevent excessive server resource consumption and potential DDoS attacks.
Use JavaScript's .bind() method to bind arguments: const boundFunction = serverFunction.bind(null, argument). This works in both Server and Client Components and supports progressive enhancement.
No. Client Components can only import Server Actions from files with the module-level 'use server' directive at the top of the file. Server Components can use either inline function-level or module-level directives.
The 'use server' directive must be placed at the top of either the async function body (function-level) or at the top of a file to mark all exports as Server Actions (module-level).
Arguments and return values must be serializable by React. Non-serializable types like functions, DOM elements, URL objects, and Date objects cannot be used without conversion.
The allowedOrigins option in serverActions configuration accepts an array of domain strings supporting wildcards, e.g., ['my-proxy.com', '*.my-proxy.com'].
The recommended profile value is 'max', which uses stale-while-revalidate semantics - the tag entry is marked as stale and fresh data is fetched only when a resource with that tag is next visited.
When cookies are set or deleted, Next.js re-renders the current page and its layouts on the server so the UI reflects the new cookie value.
No. HTTP does not allow setting cookies after streaming starts. Cookies must be set before response streaming begins.
The headers() function is read-only. You cannot set or delete outgoing request headers. To set response headers, you must return a new Response object with the desired headers.
No. Server Actions are designed for form submissions and data mutations, not streaming responses. Route Handlers are recommended for streaming use cases.
The resulting object contains extra properties prefixed with $ACTION_ (like $ACTION_REF_2, $ACTION_KEY) which are internal React/Next.js metadata that must be filtered out.
Yes. In Server Components, forms work even if JavaScript is disabled. In Client Components, forms queue submissions if JavaScript isn't loaded yet, prioritizing client hydration.
useActionState(fn, initialState, permalink?) expects an action function that receives (previousState, formData) as parameters. It returns [state, formAction, isPending].
When action is a function (Server Action), the replace and scroll props are ignored.
The scroll prop defaults to true, which scrolls to the top on navigation.
When invoked through a form, a Server Action automatically receives the FormData object as a parameter.
The prefetch prop defaults to true, which prefetches the path when the form becomes visible in the viewport.
useFormState was renamed to useActionState in React 19. useFormState is now deprecated and will be removed in a future release.
useFormStatus must be called from a component that is rendered inside a
useOptimistic takes two parameters: 1) state (the initial value), and 2) updateFn(currentState, optimisticValue) - a pure function that returns the merged optimistic state.
Call revalidatePath BEFORE redirect. Since redirect throws a control-flow exception, any code after it won't execute, so revalidation must happen first.
In Server Actions, it updates the UI immediately if viewing the affected path. In Route Handlers, the revalidation is done on the next visit to the specified path.
The type parameter defaults to 'replace' in most contexts, but defaults to 'push' when used in Server Actions.
Server Actions compare the Origin header to the Host header (or X-Forwarded-Host). If these don't match, the request is aborted. Server Actions can only be invoked on the same host as the page that hosts it.
Metadata and SEO > Platform-Specific Features
47 questionsPlace sitemap files within route segments (e.g., app/sitemap.xml and app/products/sitemap.xml) to create multiple sitemaps. Each route segment can have its own sitemap file.
No, you cannot export both the viewport object AND generateViewport function from the same route.
metadataBase should be set to a URL object using new URL('https://yourdomain.com'). It's typically set in root app/layout.js to apply across all routes.
Google, Yahoo, and Yandex are directly supported. Bing requires using the 'other' property with the 'msvalidate.01' key.
The themeColor, viewport, and colorScheme options in metadata were deprecated in favor of the viewport configuration export.
Maximum bundle size of 500KB, including JSX, CSS, fonts, images, and all other assets combined.
Static files must be named manifest.json or manifest.webmanifest. Dynamic files can be manifest.js or manifest.ts.
.ico, .jpg, .jpeg, .png, and .svg formats are supported for icon files.
No, the viewport meta tag is automatically set, and manual configuration is usually unnecessary as the default is sufficient.
The default emoji library is 'twemoji'. Other supported options are 'blobmoji', 'noto', and 'openmoji'.
Only flexbox and a subset of CSS properties are supported. Advanced layouts like display: grid will not work.
Generated sitemaps are accessible at /.../sitemap/[id] (e.g., /product/sitemap/1.xml), where [id] corresponds to the id values returned by the generateSitemaps function.
The three optional exports are: 1) alt (string for image description), 2) size (object with width and height number properties), and 3) contentType (string specifying the image MIME type).
Yes, manifest.js is a special Route Handler that is cached by default unless it uses a Dynamic API or dynamic config option.
No, you must use only the verification code itself, not the full 'google-site-verification=' prefix.
The four main properties are: canonical (for rel='canonical'), languages (for hreflang attributes), media (for responsive versions with media queries), and types (for alternative formats like RSS).
An 'absolute' path starting with '/' in a metadata field is treated as a 'relative' path and is appended to the end of metadataBase rather than replacing it entirely.
The two primary card types are 'summary_large_image' and 'app'. The summary_large_image displays title, description, and prominent imagery, while app includes app-specific fields.
The formatDetection object supports email, address, and telephone fields, all accepting boolean values to control automatic detection.
Each rule can specify: userAgent (string or array), allow (string or array of permitted paths), disallow (string or array of blocked paths), and crawlDelay (numeric value).
The themeColor option is deprecated in metadata as of Next.js 14, and should be configured in the viewport export instead.
The default values are: initialScale: 1, maximumScale: 1, and userScalable: false. The width is typically set to 'device-width'.
Generated icons support size (object with width and height number properties) and contentType (string specifying the image MIME type) as optional exports, plus standard route segment configuration options available to other Route Handlers.
Google's limit is 50,000 URLs per sitemap, requiring developers to split large applications into multiple sitemap files.
Yes, themeColor supports conditional theme colors based on user preferences using media queries like '(prefers-color-scheme: light)' and '(prefers-color-scheme: dark)'.
Yes, generated images are statically optimized (generated at build time and cached) unless they use Dynamic APIs or uncached data.
The authors field accepts an array format like authors: [{ name: 'Next.js Team', url: 'https://nextjs.org' }].
No, favicon cannot be generated via code—only as a static .ico file. However, icon and apple-icon can be generated using .js, .ts, or .tsx files.
The robots.txt file must be placed in the root of the app directory.
Numbered icon files like icon1.png and icon2.png sort lexically (alphabetically/numerically).
Supported fields are: url (required, the page address), lastModified (timestamp as string or Date object), changeFrequency, priority (numeric 0-1 range), alternates (object with language variations), images (array of image URLs), and videos (array of video metadata objects).
Supported fields include: width, initialScale, maximumScale, minimumScale, userScalable, themeColor, colorScheme, and interactiveWidget (less commonly used).
Favicon files must be placed in the root /app directory only. They cannot be located in subdirectories.
Yes, robots.js is a special Route Handler that is cached by default unless it uses a Dynamic API or dynamic config option.
The three fields are: rules (required, configuration for crawlers), sitemap (optional, URL to sitemap), and host (optional, preferred domain).
Yes, icon and apple-icon files can be located anywhere within /app/**/* (any subdirectory), unlike favicon which must be in the root /app directory.
The appleWebApp field supports title, statusBarStyle, and startupImage properties.
Forms and Mutations > Cache and Data Revalidation
46 questionsIt enables the 'use cache' directive, the cacheLife function (in conjunction with 'use cache'), and the cacheTag function.
Yes, refresh() could re-produce the same result if fetch requests are cached, since it respects existing cache configurations.
cacheLife must be placed within the function whose output is being cached.
By default, fetch requests are not cached in Next.js. You can cache individual requests by setting the cache option to 'force-cache'.
The route will be dynamically rendered, and using revalidate = 0 as a route segment config option will skip both the Full Route Cache and the Data Cache.
router.refresh() invalidates the Router Cache only. It does not change the Data Cache or Full Route Cache, as it is a client-side API.
No, revalidating the Data Cache in a Route Handler will not immediately invalidate the Router Cache because the Route Handler isn't tied to a specific route.
updateTag can only be called from within Server Actions. It cannot be used in Route Handlers, Client Components, or any other context.
No, router.refresh() does not revalidate tagged data. It only refreshes the client router without cache invalidation.
Using type='page' is a precise operation for URLs matching a page file, leaving sub-pages untouched.
revalidateTag purges cache entries in the Data Cache associated with that tag, invalidates the Full Route Cache as a consequence, and invalidates the Router Cache only when used in Server Actions (not in Route Handlers).
The type parameter is required when the path contains dynamic segments (e.g., /product/[slug]).
In Client Components, forms invoking Server Actions will queue submissions if JavaScript isn't loaded yet, prioritizing client hydration.
Using type='layout' invalidates the layout file at that segment, all nested layouts beneath it, and all pages beneath them.
When a request comes after the revalidate seconds have passed, Next.js returns the stale data immediately and fetches fresh data in the background, then updates the cache. Users need to refresh twice to see the fresh data.
The client will merge updated content without losing unaffected client-side React state (e.g., useState) or browser state (e.g., scroll position).
revalidate: false caches the resource indefinitely, and is semantically equivalent to revalidate: Infinity.
No, conflicting options such as { revalidate: 3600, cache: 'no-store' } are not allowed, and both will be ignored with a warning in development mode.
Pass an array to the next.tags option: fetch('https://...', { next: { tags: ['collection'] } })
It causes data fetching operations in the App Router to be excluded from pre-renders unless they are explicitly cached.
Yes, Server Components support progressive enhancement by default, meaning forms that call Server Actions will be submitted even if JavaScript hasn't loaded yet or is disabled.
updateTag immediately expires the cached data, and the next request will wait to fetch fresh data rather than serving stale content. revalidateTag with profile="max" serves cached data while revalidating in the background.
Currently, when you use revalidatePath inside a Server Action, it invalidates all routes in the client-side cache, causing them to refetch on navigation. This behavior is temporary and will be updated in the future to apply only to the specific path.
Revalidating the Data Cache will in turn invalidate the Full Route Cache by re-rendering components on the server. However, invalidating or opting out of the Full Route Cache does not affect the Data Cache.
The tag entry is marked as stale, and the next time a resource with that tag is visited, it will use stale-while-revalidate semantics - serving stale content while fresh data loads in the background.
revalidatePath purges the Data Cache and Full Route Cache. When called from Server Actions, it also invalidates the Router Cache.
Pass { expire: 0 } as the second argument: revalidateTag(tag, { expire: 0 })
The recommended value is "max" which uses stale-while-revalidate semantics.
stale: 300s, revalidate: 604800s (7 days), expire: 2592000s (30 days)
Routing > Route Definition
45 questionsThe children prop is an implicit slot equivalent to app/@children/page.js.
No, there cannot be a route.js file at the same route segment level as page.js. This will result in a conflict error because each file takes control of all HTTP verbs for that route.
Named slots (@team, @analytics, etc.) will trigger an error without default.js. The children implicit slot returns a 404 page if default.js is absent.
Use the @ symbol as a folder prefix: @folderName. For example, @analytics creates a slot.
No, slots are not route segments and do not affect the URL structure. For example, /@analytics/views produces the URL /views, not /@analytics/views.
When dynamicParams = false, only paths provided by generateStaticParams will be served, and unspecified routes will 404 (or match in the case of catch-all routes).
For an optional catch-all segment, params has type string[] | undefined. It can be undefined when matching the base route without parameters.
For a catch-all segment, params has type string[]. For example, { slug: ['a', 'b', 'c'] }.
The allowed values are: false, 0, or a number (representing seconds).
Templates remount when their own segment (including dynamic params) changes or when child segments at their level change. They do NOT remount when navigations occur in deeper/child segments or when search parameters change.
Yes, project files can be safely colocated inside route segments in the app directory without accidentally being routable. A route is not publicly accessible until a page.js or route.js file is added.
Page components receive two optional props: params (a promise resolving to dynamic route parameters) and searchParams (a promise resolving to URL query parameters).
Layout components receive two props: children (required, type React.ReactNode) and params (optional, a promise that resolves to an object containing dynamic route parameters).
No, layouts cannot access search parameters directly. You must use the Page's searchParams prop or Client Components with useSearchParams().
Template accepts a single required prop: children (type React.ReactNode).
A page.js (or page.tsx, page.ts, page.jsx) file is required to make a route segment publicly accessible. Without a page file, the route will not be accessible even if the folder exists.
As of v15.0.0-RC, both params and searchParams are promises that must be awaited using async/await or React's use function. They were synchronous in v14 and earlier.
default.js accepts an optional params prop—a promise that resolves to an object containing the dynamic route parameters from the root segment down to the slot's subpages.
Navigating between routes using different root layouts triggers a complete page reload (full page reload).
Wrap the folder name in square brackets: [folderName]. For example, app/blog/[slug]/page.js creates a dynamic segment.
Use three dots inside square brackets: [...folderName]. For example, app/shop/[...slug]/page.js matches /shop/a, /shop/a/b, /shop/a/b/c, etc.
loading.js automatically wraps the page.js file and any children below in a
Prefix the folder name with an underscore: _folderName. This opts the folder and all its subfolders out of routing.
Yes, the revalidate value needs to be statically analyzable. Expressions like 60 * 10 are invalid.
Next.js returns a 200 HTTP status code for streamed responses, and 404 for non-streamed responses.
During next build, generateStaticParams runs before the corresponding Layouts or Pages are generated.
During next dev, generateStaticParams runs when navigating to a route.
searchParams is a plain JavaScript object, not a URLSearchParams instance.
No, generateStaticParams is not called again during revalidation (ISR).
For a single dynamic segment, params has type string. For example, { id: 'a' }.
By default, Next.js accepts files with the extensions: .tsx, .ts, .jsx, and .js
No, route groups should not be included in the route's URL path. They exist purely for organizational purposes.
If one slot is dynamic, all slots at that level must be dynamic. Static and dynamic rendering cannot coexist at the same segment level.
If OPTIONS is not defined, Next.js will automatically implement OPTIONS and set the appropriate Response Allow header depending on the other methods defined in the Route Handler.
Use double square brackets with three dots: [[...folderName]]. For example, app/shop/[[...slug]]/page.js matches both /shop (without parameters) and /shop/a, /shop/a/b, etc.
Configuration > Caching & Data Management
45 questionsBoth options will be ignored, and in development mode a warning will be printed to the terminal.
get(cacheKey, softTags), set(cacheKey, pendingEntry), refreshTags(), getExpiration(tags), and updateTags(tags, durations).
Yes. In Next.js 14, Route Handlers that used the GET HTTP method were cached by default unless they used a dynamic function or dynamic config option.
Only in Server Functions and Route Handlers. It cannot be called in Client Components or Proxy, as it only works in server environments.
false, which is semantically equivalent to revalidate: Infinity, meaning the resource should be cached indefinitely.
It skips both the Full Route Cache and the Data Cache. Components will be rendered and data fetched on every incoming request to the server.
"max". With profile="max", the tag entry is marked as stale and uses stale-while-revalidate semantics.
updateTag immediately expires the cached data and the next request waits for fresh data. revalidateTag with profile="max" serves stale content while revalidating in the background.
30 seconds. The client router enforces a minimum 30-second stale time to ensure prefetched links remain usable.
Applying the same tag multiple times has no additional effect (idempotent behavior).
'page' or 'layout'. The type parameter is required when path contains dynamic segments.
Fetch requests are NOT cached by default. This undoes the default caching of fetch() that existed in previous versions.
No. The revalidate option is only available when using the Node.js Runtime. Using revalidate with runtime = 'edge' will not work.
The cache is stored indefinitely or until matching revalidateTag() or revalidatePath() methods are called.
Yes. The single-argument signature is deprecated and may be removed in future versions. You should use revalidateTag(tag, profile) instead.
Only from within Server Actions. It cannot be used in Route Handlers, Client Components, or other contexts.
0 seconds (not cached). This changed from 30 seconds in earlier versions to 0 seconds in Next.js 15.0.0.
No. The 'use cache: private' directive is not configurable and cannot use custom cache handlers.
In Next.js 14, fetch requests were cached by default in statically rendered routes. In Next.js 15, fetch requests are NOT cached by default - you must explicitly opt-in using cache: 'force-cache' or the fetchCache segment config.
generateStaticParams will not be called again during revalidation (ISR).
expireTag only invalidates the cache when the path is next visited, not immediately.
The lowest revalidate value across each layout and page of a single route determines the revalidation frequency of the entire route, ensuring child pages are revalidated as frequently as their parent layouts.
expirePath only invalidates the cache when the included path is next visited.
The default value is 'auto'. With this setting, Next.js fetches the resource from the remote server on every request in development, but will fetch once during next build because static prerendering occurs.
Yes. Static routes are cached by default at build time, and the rendered result (React Server Component Payload and HTML) is cached on the server.
No. Accessing dynamic data sources such as headers or cookies inside a cache scope is not supported. These must be accessed outside cached functions and passed as arguments.
Layouts and Pages > Dynamic Routing
45 questionsUse the useParams() hook from 'next/navigation' or React's use() function. The useParams hook returns values synchronously in Client Components.
No. fallback: true is not supported when using output: 'export' for static exports.
Run: npx @next/codemod@canary next-async-request-api . This codemod automates converting synchronous use of params and searchParams into asynchronous use.
An array of objects where each object has the segment name as a property with a string value. For example, for /product/[id]: { id: string }[].
When dynamicParams is true (default), the segment uses Streaming Server Rendering for dynamic segments not included in generateStaticParams.
Any paths not returned by getStaticPaths will result in a 404 page. All paths must be generated at build time.
Yes. Fetch requests with the same arguments across multiple generateStaticParams, Layouts, and Pages are automatically memoized and only made once, which decreases build times.
An array of objects where the catch-all segment property is a string array. For example, for /products/[...slug]: { slug: string[] }[].
No. During revalidation (ISR), the generateStaticParams function doesn't re-execute.
The default value is false, which means no revalidation occurs. The page is only regenerated on the next build.
Use double square brackets with ellipsis: [[...folderName]]. For example, app/shop/[[...slug]]/page.js will match /shop (without parameters), /shop/clothes, /shop/clothes/tops, and /shop/clothes/tops/t-shirts.
generateStaticParams replaces the getStaticPaths function from the Pages Router.
Yes. During development (next dev), generateStaticParams runs when navigating to routes.
The parameter will be undefined. For example, accessing /shop with [[...slug]] results in slug being undefined.
No. It is not currently possible to create advanced dynamic routes with multiple variables in a single path segment, such as unpkg.com/[email protected]. You can only have multiple segments in a path, not multiple dynamic values within one segment.
The three values are: false (returns 404 for non-generated paths), true (serves fallback version while generating in background), and 'blocking' (waits for page generation before responding).
It must always return an array, even if it's empty. Otherwise, the route will be dynamically rendered. Returning an empty array defers all rendering to runtime.
No. If using on-demand revalidation, you do not need to specify a revalidate time inside getStaticProps. Next.js will use the default value of false and only revalidate the page on-demand when res.revalidate() is called.
Params are asynchronous and returned as a Promise. You must use async/await to access the values. For example: const { slug } = await params.
No. fallback: 'blocking' is not supported when using output: 'export' for static exports.
It triggers a full page reload. For example, navigating from /cart using app/(shop)/layout.js to /blog using app/(marketing)/layout.js causes a full page reload.
Use the useRouter hook: const router = useRouter() and then access router.query.segmentName where segmentName matches the bracket name in the file.
The type is { [segmentName]: string }. For example, for [slug], params is typed as { slug: string }.
Use the @folder convention. For example, @analytics defines a slot named 'analytics'. Slots are passed as props to the shared parent layout.
Use ellipsis inside square brackets: [...folderName]. For example, app/shop/[...slug]/page.js will match /shop/clothes, /shop/clothes/tops, /shop/clothes/tops/t-shirts.
Wrap the folder name in square brackets: [folderName]. For example, app/blog/[slug]/page.js where [slug] is the dynamic segment.
The type is { [segmentName]: string[] }. For example, for [...slug], params is typed as { slug: string[] }.
Paths not generated at build time will not result in a 404. Instead, Next.js serves a fallback version of the page on first request while generating the page in the background. router.isFallback will be true during this state.
Yes. If one slot is dynamic, all slots at that level must be dynamic. This is a constraint when working with dynamic segments in parallel routes.
The App Router with generateStaticParams became stable in Next.js v13.4.0.
No. Routes in different groups should not resolve to the same URL path. For example, (marketing)/about/page.js and (shop)/about/page.js would both resolve to /about and cause an error.
Yes. The children prop is an implicit slot that does not need to be mapped to a folder. app/page.js is equivalent to app/@children/page.js.
Use router.isFallback from the useRouter hook. It will be true when the fallback version is being rendered (only when fallback: true in getStaticPaths).
An array of objects with multiple properties matching each segment. For example, for /products/[category]/[product]: { category: string, product: string }[].
Yes. You can use: const params = useParams<{ tag: string; item: string }>() to provide type safety for dynamic parameters.
The type is { [segmentName]: string[] | undefined }. For example, for [[...slug]], params is typed as { slug: string[] | undefined }.
No. Slots are not route segments and do not affect the URL structure. For example, for /@analytics/views, the URL will be /views since @analytics is a slot.
The child generateStaticParams function is executed once for each set of params the parent generates.
When a user visits a path not yet built, Next.js will build the page server-side and only send the response after it's built. No fallback state is needed, and the page is cached for subsequent requests.
Wrap the folder name in parentheses: (folderName). This indicates the folder is for organizational purposes and should not be included in the route's URL path.
No. searchParams is only available in page.js segments, not in layouts.
No. The metadata object and generateMetadata function exports are only supported in Server Components.
No. You can generate params for dynamic segments above the current layout or page, but not below.
Caching and Revalidating > Cache Mechanisms
45 questionsNo, Request Memoization doesn't apply to fetch requests in Route Handlers because they are not part of the React component tree. It only applies to generateMetadata, generateStaticParams, Layouts, Pages, and other Server Components.
The default value is 'auto' (also called 'auto no cache' in documentation). In development, fetches occur on every request. In production, fetches occur once during build for static prerendering, unless Dynamic APIs are detected on the route.
The tag entry is expired immediately, and the next request to that resource will be a blocking revalidate/cache miss. This approach is deprecated.
The type parameter (accepting 'page' or 'layout') is required when the path contains dynamic segments.
Prefetching will never happen both on entering the viewport and on hover.
Client-side stale time: 5 minutes, Server-side revalidate: 15 minutes, Expiration: No time-based expiration by default (1 year).
Using cookies(), headers(), connection(), draftMode(), searchParams, or unstable_noStore will opt the route out of the Full Route Cache and cause dynamic rendering at request time.
Yes, calling cookies.set() or cookies.delete() in Server Actions will revalidate the Router Cache.
Stale (how long the client can use cached data without checking the server), Revalidate (when to trigger a background refresh while serving cached content), and Expire (when to regenerate content synchronously on the next request with no traffic).
The Router Cache clears on page refresh. It has session persistence and survives navigation, but is cleared on page refresh.
No, the Full Route Cache is cleared on new deployments, unlike the Data Cache which persists across deployments.
The router.refresh() hook can be used to manually revalidate the Router Cache on the client side.
Use the React cache function to manually memoize functions. The cache lasts the lifetime of a server request until the React component tree has finished rendering.
- fetchData (asynchronous function to cache), 2) keyParts (optional array for cache key identification), and 3) options (optional object with 'tags' array and 'revalidate' number).
v16.0.0 with the Cache Components feature. It was introduced experimentally in v15.0.0.
Prefetching activates when a Link component enters the user's viewport (initially or through scroll). Prefetching is only enabled in production.
The default is prefetch='auto' (or null). For static routes, the full route is prefetched. For dynamic routes, only the partial route down to the nearest loading.js boundary is prefetched.
Yes, the Data Cache is persistent across incoming requests and deployments unless you revalidate or opt-out.
Yes, revalidating the Data Cache will automatically invalidate the Full Route Cache by re-rendering components on the server.
No, accessing dynamic data sources such as headers or cookies inside a cache scope is not supported. You must use headers outside the cached function and pass required data as arguments.
Only the GET and HEAD methods are automatically memoized. POST, DELETE, and other methods are not memoized.
No, cached functions cannot directly access cookies(), headers(), or searchParams. These values must be read outside the cached scope and passed as arguments.
The unstable_cache API will be replaced by 'use cache' when it reaches stability.
In Next.js 14, Route Handlers using the GET HTTP method were cached by default unless they used a dynamic function or dynamic config option. In Next.js 15, GET Route Handlers are not cached by default.
When called in Server Actions, revalidateTag() immediately invalidates the Router Cache. When called in Route Handlers, it does NOT immediately invalidate the Router Cache.
Use the route segment config option: export const dynamic = 'force-static' in your Route Handler file.
Next.js implements four distinct caching layers: Request Memoization (server-side, per-request lifecycle), Data Cache (server-side, persistent across requests/deployments), Full Route Cache (server-side, persistent HTML and RSC payload), and Router Cache (client-side, session or time-based).
Yes, if a route has a fetch request that is not cached (cache: 'no-store'), this will opt the route out of the Full Route Cache. However, other fetch requests that explicitly enable caching will still be cached in the Data Cache.
Request Memoization lasts only during a single render pass. Memory is cleared after the rendering completes, meaning it only lasts the lifetime of a server request.
Layouts and Pages > Layout Patterns and Composition
45 questionsLayout components must accept a children prop typed as React.ReactNode.
No, layouts cannot access pathname. Use the usePathname() hook in Client Components instead.
error.js wraps a route segment in a React Error Boundary, which necessitates the 'use client' directive - error boundaries must be Client Components.
You should move Client Components down your component tree and apply the 'use client' directive to individual interactive components rather than broad sections. This reduces the JavaScript bundle sent to browsers.
In a Client Component, you should use React's use hook: const { slug } = use(params).
By default, loading.js is a Server Component - but can also be used as a Client Component through the "use client" directive.
params is a promise that resolves to an object containing the dynamic route parameters from the root segment down to that layout. You must use await or React's use hook to access the values.
Suspense boundaries inside layouts only show a fallback on first load, while templates show it on every navigation.
loading.js will automatically wrap the page.js file and any children below in a <Suspense> boundary.
Parallel route slots use the @folder convention. For example, @analytics and @team are valid slot names.
No, the root layout is a Server Component by default and cannot be set to a Client Component.
Yes, you can pass Server Components as children or props to Client Components. This creates slots for server-rendered content within client-managed containers, and the server content renders before the RSC payload reaches the browser.
Yes, layouts can be async Server Components and support the async keyword for asynchronous operations.
When a file receives the 'use client' directive, all its imports and child components automatically belong to the client bundle, establishing a clear boundary between execution environments.
Navigating across multiple root layouts will cause a full page load (as opposed to a client-side navigation). This only applies to multiple root layouts.
No, layouts preserve state, remain interactive, and do not re-render during navigation. This is called partial rendering.
No, metadata and generateMetadata exports are not supported in global-error.jsx.
When placed in a folder alongside layout.js, the loading component nests inside the layout file. The layout remains interactive while new route segments load.
Route groups use parenthesis convention: (folderName). The parentheses signal that the folder is for organizational purposes.
Global error UI must define its own <html> and <body> tags, since it is replacing the root layout or template when active.
No, search params do not trigger remounts of templates. Templates only remount when their specific route segment or dynamic parameters change.
By default, layouts in the folder hierarchy are nested, which means parent layouts wrap child layouts via their children prop. For example, the root layout (app/layout.js) would wrap the blog layout (app/blog/layout.js), which would wrap the blog pages.
To handle errors within a specific layout or template, place an error.js file in the layout's parent segment.
No, an error.js boundary will not handle errors thrown in a layout.js component in the same segment because the error boundary is nested inside that layout's component.
No, layouts cannot pass data to children. However, you can fetch the same data multiple times and React will automatically deduplicate the requests using React's cache() or Next.js's automatic request deduplication.
No, slots are not route segments and do not affect the URL structure. For example, for /@analytics/views, the URL will be /views.
In terms of nesting, template.js is rendered between a layout and its children. The hierarchy is: <Layout><Template key={routeParam}>{children}</Template></Layout>.
No, route groups are excluded from URL paths. Routes inside (marketing)/about/page.js resolve to /about, not /marketing/about.
No, routes in different groups should not resolve to the same URL path, as this will cause errors.
No, React context cannot be used directly in Server Components; it requires a Client Component wrapper that provides context to child elements.
No, you should not manually add <head> tags such as <title> and <meta> to root layouts. Instead, you should use the Metadata API which automatically handles advanced requirements such as streaming and de-duplicating <head> elements.
The default.js file serves as a fallback component during initial loads and full-page refreshes. It renders when Next.js cannot determine the active state for unmatched slots, preventing 404 errors.
You should await the params: const { team } = await params in async Server Component layouts.
While layouts persist across routes and maintain state, templates are assigned unique keys that cause child components to reset on navigation. DOM elements inside the template are fully recreated during navigation.
Yes, layouts are Server Components by default but can be set to a Client Component (except for the root layout).
Providers should be rendered as deep as possible in the component tree for optimal Next.js optimization, rather than wrapping the entire application.
Slots are passed as props to the shared parent layout. For example, if you have @analytics and @team slots, the layout receives them as analytics and team props typed as React.ReactNode.
No, you cannot have separate static and dynamic slots at the same route segment level. If one slot is dynamic, all slots at that level must be dynamic.
No, layouts cannot access search params because they do not re-render on navigation, which would cause the search params to become stale.
Rendering Strategies > Performance Optimization Techniques
44 questionsfalse. This uses the default heuristic to cache any fetch requests that set their cache option to 'force-cache' or are encountered before Dynamic APIs activate.
The default value is 50MB. You can configure it in next.config.js under experimental.isrMemoryCacheSize. Setting it to 0 disables the in-memory cache, which is useful for Kubernetes deployments with multiple pods sharing a file system cache.
No. Server Components run on the server and don't require JavaScript to render on the client, and have no impact on the size of your client-side JavaScript bundles.
'nodejs'. This is the recommended option for rendering applications.
30 seconds. This minimum is enforced to ensure prefetched links remain usable and prevents prefetched data from expiring before users can click on links.
If an error is thrown while attempting to revalidate data, the last successfully generated data will continue to be served from the cache. On the next subsequent request, Next.js will retry revalidating the data.
5 minutes (300 seconds). This applies to statically generated pages or when the prefetch prop on Link is set to true.
1024 bytes. Some browsers buffer a streaming response and may not show the streamed response until it exceeds 1024 bytes. This typically only affects 'hello world' applications, not real applications.
Prefetching is only enabled in production. In development mode, prefetching does not occur.
Both options will be ignored. Conflicting options are not allowed and Next.js will ignore both settings.
It caches the resource indefinitely, which is equivalent to revalidate: Infinity.
'auto'. The system will cache fetch requests before Dynamic APIs with the cache option they provide and not cache fetch requests after Dynamic APIs.
Next.js maintains a small task queue that prefetches in the following order: 1) Viewport-visible links, 2) Links showing user intent (hover/touch), 3) Newer links replace older ones, 4) Off-screen links are discarded.
PPR sends the full response—including static HTML and streamed dynamic parts—in a single HTTP request, avoiding extra roundtrips and improving both initial load and overall performance.
200 status code. When streaming content, the system consistently returns a 200 status code to indicate successful requests, even when errors occur downstream in the streamed content itself.
null (also referred to as 'auto'). With this default, static routes will be fully prefetched, while dynamic routes will only prefetch down to the nearest segment with a loading.js boundary.
experimental.webpackMemoryOptimizations: true in next.config.js. This reduces max memory usage but may increase compilation times by a slight amount.
true. When set to true, dynamic segments not included in generateStaticParams are generated on demand. When set to false, they return a 404.
It controls the ppr, useCache, and dynamicIO flags as a single, unified configuration.
It is enabled by default when running next build if dynamic APIs are not used. Routes are statically rendered and data requests are cached unless you opt out. No configuration is required.
30 seconds (configurable). For dynamic routes, only the shared layout down to the first loading.js file is prefetched and cached for 30 seconds.
It updates the UI immediately (if viewing the affected path), but causes all previously visited pages to refresh upon navigation—a temporary behavior subject to future changes.
It ensures all fetch requests opt out of caching by changing the default to cache: 'no-store' if no option is provided and causes an error if any fetch requests use cache: 'force-cache'.
Only in Server Functions and Route Handlers. It cannot be called in Client Components or Proxy, as it only works in server environments.
Next.js will cache as much as possible to improve performance and reduce cost. This means routes are statically rendered and data requests are cached unless you opt out.
experimental: { ppr: 'incremental' } in next.config.ts. Routes then require explicit opt-in via export const experimental_ppr = true.
Only with Node.js runtime (default). ISR is incompatible with Static Export deployment.
The default value is 50MB. This controls the size of the default in-memory cache. You can set cacheMaxMemorySize to 0 to disable default in-memory caching, typically when using a custom cache handler.
0 seconds (not cached by default). The dynamic staleTimes default changed from 30 seconds to 0 seconds in Next.js 15.0.0.
Cache persists until app reload when there is no loading.js boundary.
It ensures all fetch requests opt out of caching by setting the cache option of all fetch requests to 'no-store'. This forces all fetch requests to be re-fetched every request even if they provide a 'force-cache' option.
'auto'. This is the default option to cache as much as possible without preventing any components from opting into dynamic behavior.
It is equivalent to setting the option of every fetch() request in a layout or page to { cache: 'no-store', next: { revalidate: 0 } }.
It is enabled automatically for applications without custom Webpack configurations.
The lowest time will be used. If you have multiple fetch requests with different revalidate frequencies, the lowest time is applied to all requests.
Upgrading > Configuration & Package Reorganization
44 questions.next/dev (changed from .next to enable concurrent dev and build execution)
23 popular packages including lucide-react, @mui/material, react-icons/*, date-fns, lodash-es, @headlessui/react, @heroicons/react, @tabler/icons-react, recharts, and others.
Yes. Use environment variables instead of serverRuntimeConfig, and use NEXT_PUBLIC_ prefix instead of publicRuntimeConfig.
true. The .next folder is now cleared by default on build, preserving only Next.js caches.
true (enabled by default). This stores compiler artifacts on disk between runs for faster compile times.
npm install next@latest react@latest react-dom@latest (plus npm install -D @types/react @types/react-dom babel-plugin-react-compiler for TypeScript projects)
babel-plugin-react-compiler (install with: npm install -D babel-plugin-react-compiler)
Next.js 15. The swcMinify option was removed entirely as SWC became the only minifier.
The target property was removed entirely in Next.js 13, superseded by Output File Tracing.
false (disabled by default). Local IP optimization is blocked by default in Next.js 16.
It moved from experimental.turbopack to the top-level turbopack property in next.config.js
Yes. Builds fail if webpack config exists to prevent misconfiguration, unless you use the --webpack flag.
Yes. Turbopack is stable and used by default with next dev and next build in Next.js 16.
Yes. All AMP APIs including useAmp hook and amp config have been removed in Next.js 16.
Experimental. It remains under the experimental configuration namespace.
true (enabled by default). This separates development and production build outputs into different directories.
'edge'. The runtime configuration value 'experimental-edge' now causes an error and must be updated to 'edge'.
Yes, the search field is required for local images with query strings starting in Next.js 16 for security reasons. Omitting it allows all query strings which could allow malicious actors to optimize URLs you did not intend.
false (disabled by default). You must explicitly set it to true to enable automatic bundling.
Layouts and Pages > Route Structure and Nesting
43 questionsYes. Project files can be safely colocated inside route segments in the app directory. Even though route structure is defined through folders, a route is not publicly accessible until a page.js or route.js file is added to a route segment.
Parallel routes use the @folder convention. For example, @analytics and @team define two named slots.
global-error.js must define its own and
tags, as it replaces the root layout when active.The child generateStaticParams function is executed once for each set of params the parent generates.
For a single segment, params is { slug: string }. The value is a string.
For a catch-all segment, params is { slug: string[] }. The value is an array of strings.
In Next.js 15, both params and searchParams are Promises: Promise<{ [key: string]: string | string[] | undefined }>. You must use async/await or React's use() function to access values.
Yes. Accessing searchParams automatically opts pages into dynamic rendering at request time.
The /public directory, configuration files (package.json, next.config.js, tsconfig.json), and environment files (.env.*) must remain in the root of your project.
If one slot is dynamic, all slots at that level must be dynamic. You cannot mix static and dynamic slots at the same segment level.
Error boundaries must be Client Components. The 'use client' directive is mandatory.
No. Route groups do not affect the URL structure. A file at app/(marketing)/about/page.js resolves to /about, not /marketing/about.
No. On navigation, layouts preserve state, remain interactive, and do not rerender.
No. not-found.js or global-not-found.js components do not accept any props.
To create URL segments that start with an underscore, prefix the folder name with %5F (the URL-encoded form of an underscore): %5FfolderName.
A route is not publicly accessible until a page.js or route.js file is added to a route segment.
Route groups are created by wrapping a folder's name in parentheses: (folderName). This indicates the folder is for organizational purposes and should not be included in the route's URL path.
Navigating across multiple root layouts will cause a full page load (as opposed to a client-side navigation).
Layouts persist across route transitions and maintain state, whereas templates receive unique keys and remount on every navigation, resetting all state.
Catch-all segments use [...folderName]. For example, app/shop/[...slug]/page.js matches /shop/a, /shop/a/b, /shop/a/b/c, etc.
.js, .jsx, or .tsx file extensions can be used for page, layout, and other special files.
In Next.js 14 and earlier, params and searchParams were synchronous props that could be accessed directly without async/await.
Slots are passed as props to the shared parent layout alongside the implicit children prop.
This causes an error. Routes in different groups cannot resolve to identical URLs.
Yes. When using fetch inside the generateStaticParams function, the requests are automatically deduplicated, avoiding multiple network calls for the same data across Layouts, Pages, and other generateStaticParams functions.
No. Layouts cannot access or update query parameters (searchParams). To handle dynamic data on navigation, use Client Components with hooks like useSearchParams().
For an optional catch-all segment with no additional segments, params is { slug: undefined }.
loading.js will automatically wrap the page.js file and any children below in a
If app or pages are present in the root directory, src/app or src/pages will be ignored. The root-level routing folders take priority.
When dynamicParams = false, only paths provided by generateStaticParams will be served, and unspecified routes will 404 or match (in the case of catch-all routes).
Private folders are created by prefixing a folder with an underscore: _folderName. This indicates the folder is a private implementation detail and should not be considered by the routing system, thereby opting the folder and all its subfolders out of routing.
Yes. Since children is an implicit slot, you also need to create a default.js file to render a fallback for children when Next.js cannot recover the active state of the parent page.
Single dynamic segments use square brackets: [folderName]. For example, app/blog/[slug]/page.js creates a dynamic route.
page.js is used to create UI pages that users can navigate to in the browser, while route.js is used to create custom request handlers for API endpoints.
Templates remount when their segment level changes or dynamic parameters update. They do not remount on search parameter changes or navigation within deeper segments.
Optional catch-all segments use [[...folderName]]. For example, app/shop/[[...slug]]/page.js matches both /shop and /shop/a, /shop/a/b, etc.
Testing > Testing Strategies
43 questionsYes, Playwright launches headless browsers by default. Headless browsers don't display a UI, so you must use the command line instead.
npx create-next-app@latest --example with-playwright with-playwright-app
Next.js recommends five types of tests: (1) Unit Testing - testing individual units (or blocks of code) in isolation, (2) Component Testing - a specialized unit testing approach focusing on React components, (3) Integration Testing - testing how multiple units work together, (4) End-to-End Testing - testing user flows in an environment that simulates real user scenarios, and (5) Snapshot Testing - comparing current component output against previously saved snapshots.
http://localhost:3000 - Adding this to the e2e configuration allows you to use cy.visit('/') instead of cy.visit('http://localhost:3000/').
Yes, Vitest will watch for changes in your project by default in interactive environments. In CI or non-interactive shells, watch mode is not the default but can be enabled explicitly.
npm install -D cypress, yarn add -D cypress, or pnpm install -D cypress
Next.js will not load environment variables from .env.development or .env.production in the testing environment, and .env.local won't be loaded to ensure tests produce the same results for everyone.
No. Component tests do not require a Next.js server, so features like
Next.js recommends using End-to-End Testing over Unit Testing for async components, because async Server Components are new to the React ecosystem and some tools do not fully support them.
http://localhost:3000 - This simplifies navigation commands by allowing relative paths instead of full URLs.
Jest looks for .js, .jsx, .ts and .tsx files inside of tests folders, as well as any files with a suffix of .test or .spec (e.g. Component.test.js or Component.spec.js). It will also find files called test.js or spec.js.
vitest, @vitejs/plugin-react, jsdom, @testing-library/react, @testing-library/dom, and vite-tsconfig-paths.
plugins must include react() plugin. TypeScript projects should also include tsconfigPaths().
Cypress 13.6.3 or later. Versions below 13.6.3 do not support TypeScript version 5 with moduleResolution:bundler.
No. Cypress currently doesn't support Component Testing for async Server Components.
setupFilesAfterEnv is a list of paths to modules that run code to configure or set up the testing framework before each test file in the suite is executed. It's typically used to automatically load jest-dom in every test.
It's recommended to put test files (.test.js/.spec.js files or tests folders) next to the code they are testing so that relative imports appear shorter. However, test files in the pages/ directory can cause build errors unless you configure the pageExtensions option.
No. Jest currently does not support async Server Components. While you can still run unit tests for synchronous Server and Client Components, Next.js recommends using E2E tests for async components.
next/jest auto-mocks stylesheets (.css, .module.css, and their scss variants), image imports, and next/font.
Next.js recommends: Cypress for E2E and Component Testing, Jest for Unit and Snapshot Testing, Playwright for E2E Testing, and Vitest for Unit Testing.
Jest's moduleNameMapper must match the paths option in tsconfig.json or jsconfig.json. For example, if tsconfig.json has "@/components/": ["components/"], then Jest's moduleNameMapper should have '^@/components/(.*)$': '
No. Vitest currently does not support async Server Components. Next.js recommends using E2E tests for those instead.
Use loadEnvConfig from @next/env in a Jest global setup file or in jest.config.js: import { loadEnvConfig } from '@next/env'; loadEnvConfig(process.cwd())
The webServer feature automatically starts the development server and waits for it to be fully available before running tests. It's particularly useful for Next.js applications during testing.
start-server-and-test is used to run the Next.js production server in conjunction with Cypress. After installation, you add a script like "test": "start-server-and-test start http://localhost:3000 cypress" to package.json.
npx playwright install-deps - This installs all the Playwright system dependencies.
vitest.config.mts (for TypeScript projects) or vitest.config.js (for JavaScript projects).
Unit Testing focuses on testing individual units (or blocks of code) in isolation, where a unit can be a single function, hook, or component. Component Testing is a more focused version of unit testing where the primary subject is React components, testing how they render, interact with props, and behave in response to user events.
jest, jest-environment-jsdom, @testing-library/react, @testing-library/dom, @testing-library/jest-dom, ts-node (for TypeScript), and @types/jest (for TypeScript).
!process.env.CI - This allows the local dev server to reuse the existing server when running tests locally but does not use an existing server on CI.
Playwright tests run against three browsers: Chromium, Firefox, and WebKit.
npm init playwright, yarn create playwright, or pnpm create playwright
next/jest automatically: (1) sets up transform using the Next.js Compiler, (2) loads .env files into process.env, (3) loads next.config.js for flags that enable SWC transforms, (4) ignores node_modules and .next from test resolving, and (5) auto-mocks stylesheets, images, and next/font.
cypress run - This runs tests in headless mode without the interactive UI.
Server Actions > Data Management
43 questionsNo. Server Actions cannot be defined inside Client Components. To use Server Actions in Client Components, you must create them in a dedicated file using the 'use server' directive at the file level and then import them.
Use the serverActions.bodySizeLimit option in next.config.js. It accepts a number of bytes (e.g., 1000) or string format supported by the bytes library (e.g., '500kb' or '3mb'). Example: module.exports = { experimental: { serverActions: { bodySizeLimit: '2mb' } } }
Yes. The headers() function allows you to read incoming request headers but is read-only. You cannot set or delete outgoing request headers in Server Actions.
Yes. .bind() of a Server Action works in both Server and Client Components, and it supports progressive enhancement.
No. redirect() throws a NEXT_REDIRECT error and terminates rendering, so it must be called outside try/catch blocks. If caught in a try/catch, the redirect will be treated as an error.
'push'. In Server Actions, redirect() defaults to 'push' (adding a new entry to browser history). In Server Components, Client Components, and Route Handlers, it defaults to 'replace' (replacing the current URL).
The entire client cache is immediately cleared, bypassing the stale time.
POST. Server Actions use the POST method exclusively, and only this HTTP method can invoke them.
Next.js re-renders the current page and its layouts on the server so the UI reflects the new cookie value.
307 (Temporary Redirect) for most contexts. For permanent redirects, use the permanentRedirect() function which returns 308.
The single-argument form revalidateTag(tag) is deprecated. You should use the two-argument form with a profile parameter, such as revalidateTag(tag, 'max').
Use the bind() method to create a new Server Action with preset arguments. For example: const updateUserWithId = updateUser.bind(null, userId). The first argument to bind() is the context (usually null), followed by the arguments to bind.
No. Server Actions are designed for server-side mutations. The client currently dispatches and awaits them one at a time (sequentially).
Use the serverActions.allowedOrigins configuration option in next.config.js. It accepts an array of strings with domain patterns (e.g., ['*.my-proxy.com']) to specify extra safe origin domains.
No. For expected errors like server-side form validation or failed requests, avoid using try/catch blocks. Instead, model them as return values (e.g., return an object with success/error status).
Yes. Server Components support progressive enhancement by default, meaning forms that call Server Actions will be submitted even if JavaScript hasn't loaded yet or is disabled. In Client Components, forms will queue submissions if JavaScript isn't loaded yet.
Yes, but there have been reported inconsistencies. unstable_noStore() is equivalent to cache: 'no-store' on a fetch and can be used to declaratively opt out of static rendering. However, some developers have needed alternative approaches like calling headers() or cookies() functions.
Call revalidatePath() or revalidateTag() before calling redirect() to ensure fresh data is available. Code after redirect() won't execute because redirect() throws an exception.
Two locations: (1) At the top of an async function to mark only that function as a Server Action, or (2) at the top of a file to mark all exports of that file as Server Actions. File-level placement is required when using Server Actions in Client Components.
You must enable the cacheComponents flag in next.config.js before using cacheTag().
Use formData.get('fieldName') and cast it as File. For example: const file = data.get('fileUpload') as File. You can then convert it to a Buffer using Buffer.from(await file.arrayBuffer()).
The type parameter is required when the path contains a dynamic segment (e.g., '/product/[slug]'). If the path is a specific URL like '/product/1', you should omit the type parameter.
No. updateTag() can only be called from within Server Actions. Attempting to use it elsewhere will throw an error.
Asynchronous. The cookies() function must be awaited: const cookieStore = await cookies(). This represents a change from earlier versions where it was synchronous.
'page' or 'layout'. When using 'page', it will revalidate any URL that matches the provided page file but will not invalidate pages beneath it. When using 'layout', it will revalidate any URL that matches the provided layout file and cause pages beneath with the same layout to revalidate.
The captured variables are sent to the client and back to the server when the action is invoked. Next.js automatically encrypts these closed-over variables to prevent sensitive data from being exposed to the client.
307 preserves the original request method as POST, whereas many browsers changed the request method from POST to GET when using 302.
No. The cookies() function is read-only in Server Components. You can only set or delete cookies in Server Actions or Route Handlers where response headers can be properly set.
'max'. By default, cacheLife accepts the following values: 'seconds', 'minutes', 'hours', 'days', 'weeks', and 'max'.
Any code after redirect() won't execute because redirect() throws a framework-handled control-flow exception that terminates rendering.
updateTag() immediately expires the cache entry and can only be used in Server Actions (for read-your-own-writes scenarios). revalidateTag() can be used in Server Actions and Route Handlers and supports stale-while-revalidate semantics with profile='max'.
Yes. You can use startTransition from the useTransition hook to invoke Server Actions from event handlers, useEffect, or third-party libraries, though this disables progressive enhancement.
'max'. When using profile='max', the tag entry is marked as stale, and the next time a resource with that tag is visited, it will use stale-while-revalidate semantics, serving stale content while fetching fresh data in the background.
Yes. When a Server Action is created and exported, it creates a public HTTP endpoint and should be treated with the same security assumptions. You should always ensure users are authorized to perform an action and re-authorize users inside the action.
1MB. This limit is designed to prevent excessive server resource consumption and potential DDoS attacks.
It immediately expires the cached data for the specified tag. The next request will wait to fetch fresh data rather than serving stale content, ensuring users see their changes immediately.
When invoked through forms, Server Actions automatically receive the FormData object as a parameter, allowing developers to extract values using native FormData methods like formData.get().
OpenTelemetry
43 questionsIt automatically configures the best export mechanism for the environment. On Vercel with a tracing integration configured, it uses that integration automatically. Otherwise, it can use an OTLP exporter if configured via environment variables.
trace.getTracer('tracer-name').startActiveSpan('span-name', async (span) => { ... })
@vercel/otel, @opentelemetry/sdk-logs, @opentelemetry/api-logs, and @opentelemetry/instrumentation
In the root of your project (not inside app or pages directories). If using the src folder, place it inside src alongside pages and app.
Yes, Next.js itself is already instrumented, meaning it automatically generates spans for key operations once you enable instrumentation.
BatchSpanProcessor. SimpleSpanProcessor should be avoided in production as it exports spans synchronously and causes significant performance overhead.
You must update the instrumentation filename to match the pageExtensions suffix
propagators (by default, @vercel/otel configures W3C Trace Context propagator)
Create instrumentation.node.ts for Node.js-specific code, then conditionally import it from instrumentation.ts with: if (process.env.NEXT_RUNTIME === 'nodejs') { await import('./instrumentation.node.ts') }
traceSampler (e.g., traceSampler: new TraceIdRatioBasedSampler(0.1))
ATTR_SERVICE_NAME (SEMRESATTRS_ and SEMATTRS_ prefixed constants were deprecated as of version 1.26.0)
attributes (by default, @vercel/otel configures relevant Vercel attributes like env, vercel.runtime, vercel.host based on the environment)
Configuration > Project Structure & Format
43 questionsNo. The public directory must remain in the root of your project and cannot be renamed. It cannot be moved inside src.
Next.js supports .js, .mjs, and .ts for next.config files. .cjs and .cts are explicitly NOT supported.
The supported methods are: GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS.
Use [[...segment]] (e.g., [[...slug]]). This matches the route without any parameter as well as with multiple segments.
It receives: 1) children (required) - to render nested route segments, and 2) params (optional) - a promise resolving to dynamic route parameters that must be awaited.
The /public directory, package.json, next.config.js, tsconfig.json, and .env.* files must remain in the root of your project.
No. Routes in different groups should not resolve to the same URL path. For example, (marketing)/about/page.js and (shop)/about/page.js would both resolve to /about and cause an error.
Use [...segment] (e.g., [...slug]). This matches nested paths of any depth but requires at least one segment to be present.
No. A route is NOT publicly accessible until a page.js or route.js file is added to a route segment. Project files can be safely colocated inside route segments without accidentally becoming routable.
If default.js doesn't exist for named slots during hard navigation, an error is returned. You must define a default.js to continue.
It must be placed in the root of your project at the same level as pages or app, OR inside src if using the src folder (e.g., src/middleware.ts).
The top-level folders are: app (App Router), pages (Pages Router), public (static assets), and src (optional application code separation).
The opengraph-image file size must not exceed 8MB. If exceeded, the build will fail.
It must be in the project root directory (not inside app or pages), OR inside src/ if using the src folder structure.
From highest to lowest priority: 1) process.env, 2) .env.[NODE_ENV].local, 3) .env.local (not loaded when NODE_ENV is test), 4) .env.[NODE_ENV], 5) .env
It triggers a full page reload (not a client-side navigation). This only applies when navigating between multiple root layouts.
Prefix the folder name with an underscore: _folderName. This indicates the folder is a private implementation detail and should not be considered by the routing system.
Yes. The app directory MUST include a root layout - it is mandatory.
Yes. Layouts can be async functions as demonstrated in the official documentation examples.
Static: robots.txt, or programmatically generated: robots.js or robots.ts.
Yes. global-error.js must define its own <html> and <body> tags because it replaces the root layout when active.
The twitter-image file size must not exceed 5MB. If exceeded, the build will fail.
For .js files: CommonJS with module.exports. For .mjs and .ts files: ESM with export default. It is a regular Node.js module, not a JSON file.
Yes. The default extensions (.tsx, .ts, .jsx, .js) can be modified using the pageExtensions configuration option in next.config.js to allow other extensions like .md or .mdx.
No. You cannot generate a favicon icon programmatically - it must be a static file. However, icon and apple-icon can be generated using .js, .ts, or .tsx files.
In the root of the app directory (e.g., app/robots.txt or app/robots.js).
Supported formats: .jpg, .jpeg, .png (SVG is NOT supported for apple-icon).
Officially supported: route.js and route.ts. While route.jsx and route.tsx technically work, they are not recommended unless JSX is needed (e.g., for rendering emails).
Layouts persist across routes and maintain state without re-rendering. Templates are fully remounted on every navigation, recreating DOM elements and resetting state.
Next.js returns a 200 HTTP status code for streamed responses, and 404 for non-streamed responses.
In the root of the app directory (e.g., app/sitemap.xml or app/sitemap.js).
Yes. In Next.js 15, params is a promise. You must use async/await in Server Components or React's use() function in Client Components to access its values.
No. Only the root layout must define and
tags. Nested layouts do not need to define them.Yes. Only assets that are in the public directory at build time will be served by Next.js. Files added after build will not be served.
No. You should NOT manually add
tags such asThe favicon image can ONLY be located in the top level of app/ directory - it cannot be placed in subdirectories.
Wrap the folder name in parentheses: (folderName). This indicates the folder is for organizational purposes and should not be included in the route's URL path.
The default order is: .tsx, .ts, .jsx, .js (TypeScript extensions take priority over JavaScript).
Yes. Error boundaries must be Client Components - they require the 'use client' directive at the top of the file.
No. There cannot be a route.js file at the same route segment level as page.js - this creates a conflict.
Routing > Backend Integration
42 questionsYes, using cookies() triggers dynamic rendering. Components only opt into dynamic rendering when the value is accessed.
An extension of the Web Request API that provides additional control including easily accessing cookies and an extended, parsed URL object nextUrl
No, the revalidate value is not available when using runtime = 'edge'.
No, you cannot have both route.js and page.js at the same route segment level. Having both will cause a conflict error.
You must call revalidate() on the exact path (not the rewritten path). For example, if you have a rewrite from /post-1 -> /blog/post-1, you would need to call res.revalidate('/blog/post-1').
Yes, for example revalidate = 600 is valid, but revalidate = 60 * 10 is not.
No, calling revalidatePath or revalidateTag will not immediately trigger many revalidations at once.
In Server Functions and Route Handlers. It cannot be used in Client Components or Proxy environments.
Yes, API Routes do not specify CORS headers, meaning they are same-origin only by default.
No, use cache cannot be used directly inside a Route Handler body; you must extract it to a helper function.
No, it marks the path for revalidation on the next visit. Calling revalidatePath with a dynamic route segment will not immediately trigger many revalidations at once.
json(body, options?), redirect(url), rewrite(url), and next(options?)
'page' or 'layout'. This parameter is required when the path contains dynamic segments.
No, API Routes from Pages Router are not supported with static exports. However, App Router Route Handlers can be used with static exports (GET method only).
set(name, value), get(name), getAll(name?), has(name), and delete(name)
Optional catch-all routes [[...slug]] will also match the base path without parameters (e.g., /shop), while regular catch-all routes [...slug] require at least one parameter.
req.cookies (object containing cookies, defaults to {}), req.query (object containing query string, defaults to {}), and req.body (parsed request body based on content-type, or null if no body was sent)
In Server Functions (Server Actions) and Route Handlers. It cannot be called in Client Components or Proxy environments.
Export a maxDuration constant with the timeout value in seconds (e.g., export const maxDuration = 30;)
Next.js recommends using the Node.js runtime for rendering your application.
Yes, the single-argument form is deprecated and may be removed in future versions. Use revalidateTag(tag, profile) instead.
Only in Server Actions and Route Handlers. You cannot set cookies directly in a Server Component (read-only there).
res.status(code), res.json(body), res.send(body), res.redirect([status,] path), and res.revalidate()
'auto' (default), 'default-cache', 'only-cache', 'force-cache', 'force-no-store', 'default-no-store', 'only-no-store'
Predefined/static routes (e.g., /api/post/create.js), then dynamic routes (e.g., /api/post/[pid].js), then catch-all routes (e.g., /api/post/[...slug].js)
TypeScript > Data Fetching Types
41 questionsThe req property is of type http.IncomingMessage & { cookies: NextApiRequestCookies }
Import GetServerSideProps and InferGetServerSidePropsType from 'next': import type { GetServerSideProps, InferGetServerSidePropsType } from 'next'
No, .set() and .delete() can only be used in Server Actions or Route Handlers, not in Server Components. HTTP does not allow setting cookies after streaming starts
The revalidateReason property is of type 'build' | 'stale' | 'on-demand'
Use the satisfies operator: export const getStaticProps = (async (context) => { ... }) satisfies GetStaticProps<{ repo: Repo }>
Import Metadata and ResolvingMetadata from 'next': import type { Metadata, ResolvingMetadata } from 'next'
generateStaticParams should return an array of objects: Array<{ [paramName: string]: string | string[] }>. For example: [{ slug: 'post-1' }, { slug: 'post-2' }]
For catch-all routes [...slug], params is Promise<{ slug: string[] }>. For optional catch-all [[...slug]], it can also be Promise<{ slug: string[] | undefined }>
The resolvedUrl property is of type string representing the normalized request URL
For nested routes, the params argument is typed as { params: { [parentParam: string]: string } }. For example: { params: { category: string } }
headers() returns Promise<ReadonlyHeaders> (it is an async function that returns a read-only Web Headers object)
No, there is a known limitation where InferGetServerSidePropsType breaks and returns never when using conditional returns like redirect or notFound
Import GetStaticProps from 'next': import type { GetStaticProps } from 'next'
Import GetStaticPaths from 'next': import type { GetStaticPaths } from 'next'
Each params object is of type Record<string, string | string[] | null | undefined | false>
The locales property is of type string[] | undefined (only present if i18n is enabled)
searchParams is typed as Promise<{ [key: string]: string | string[] | undefined }>
The params property is of type Params | undefined, where Params is a generic parameter containing route parameters as an object
The revalidate property can be number (seconds), false (no revalidation), or boolean
CookieStore has: get(name), getAll(), has(name), set(name, value, options), delete(name), and toString()
For optional catch-all routes [[...slug]], params can be string[] or undefined (e.g., Promise<{ slug: string[] | undefined }>)
The fallback property can be false, true, or 'blocking' (string literal)
searchParams is a Promise and must be accessed with async/await or React's use() function
The locale property is of type string | undefined (only present if i18n is enabled)
PageProps is a globally available helper type that enables autocomplete and strict keys for params: PageProps<'/blog/[slug]'> provides type-safe params for that route
The satisfies operator was added in TypeScript version 4.9
Import NextRequest from 'next/server' and use NextResponse from 'next/server': import { type NextRequest, NextResponse } from 'next/server'
RouteContext is a globally available helper that provides strongly typed params from a route literal: RouteContext<'/users/[id]'> enables type-safe access to route parameters
In Next.js 15, params is a Promise and must be accessed with async/await or React's use() function
Middleware is typed as: export function middleware(req: NextRequest, event: NextFetchEvent) { return NextResponse.next() }
ReadonlyHeaders has: get(name), has(name), entries(), keys(), values(), and forEach(callback) (read-only Web Headers API)
searchParams is only available in page.js segments, not in layout.js or other component types
Params should extend ParsedUrlQuery from 'querystring': interface IParams extends ParsedUrlQuery { slug: string }
Import ParsedUrlQuery from 'querystring': import { ParsedUrlQuery } from 'querystring'
Routing > Advanced Routing Patterns
41 questionsThe default.js file serves as a fallback component in parallel routes when Next.js cannot recover a slot's active state following a full-page load (hard navigation).
Yes. The returned segments include Route Groups, which you might not want to be included in your UI. Use JavaScript's filter() method to remove items starting with brackets.
Yes. The children implicit slot requires default.js to prevent 404 responses when active state cannot be recovered.
Route groups are created by wrapping a folder's name in parentheses: (folderName). For example, (marketing) or (shop).
No. The folder is for organizational purposes and should not be included in the route's URL path. Route groups are transparent to URL generation.
Use [[...folderName]] with three dots inside double square brackets. For example, app/shop/[[...slug]]/page.js
No. If one slot is dynamic, all slots at that level must be dynamic. Static and dynamic slots cannot coexist at the same route segment level.
Layouts persist across routes and maintain state, while templates receive unique keys and reset child component state on navigation.
No. Search parameter changes do not trigger remounts for template.js files.
Yes. You can add a layout inside a slot to allow users to navigate the slot independently. This is useful for creating tabs with sub-navigation.
The children prop is an implicit slot that does not need to be mapped to a folder. app/page.js is equivalent to app/@children/page.js.
Yes. In version 14 and earlier, params was a synchronous prop. This synchronous access is deprecated in Next.js 15.
Navigating across multiple root layouts will cause a full page load (as opposed to a client-side navigation).
In Next.js 15, the params prop is a Promise and must be accessed using async/await or React's use() function.
It causes an error. Routes in different groups should not resolve to the same URL path. For example, (marketing)/about/page.js and (shop)/about/page.js both producing /about generates an error.
Any Client Component inside the template will reset its state on navigation. Effects automatically re-synchronize as the component remounts, and DOM elements are fully recreated.
You can close the modal by calling router.back() or by using the Link component with the replace prop.
No. app/shop/[...slug]/page.js will match /shop/clothes and /shop/clothes/tops, but cannot match the base route /shop alone.
Returns values as an array. The route /shop/a/b/c produces { slug: ['a', 'b', 'c'] }.
Templates remount when their segment level changes, including dynamic parameters. They only remount if their own segment or any child segment within their level changes—not for deeper navigations or search parameter changes.
Use (..) to match segments one level above. For example, (..)photo intercepts the photo segment one level up.
No. Slots are not route segments and do not affect the URL structure. The URL reflects only the regular page path, not slot names.
During hard navigation (page refresh), unmatched slots render their default.js file. If no default.js exists, a 404 is displayed.
Interception happens during client-side navigation (soft navigation) when users click links within the application. It does NOT happen when navigating via shareable URLs, direct URL entry, or page refresh (hard navigation).
Returns a string of the active segment one level below the Layout it is called from, or null if none exists.
Returns an array of strings containing active segments one level below the calling layout. Returns an empty array if no child segments exist.
No. It is a Client Component hook. Since layouts are Server Components by default, useSelectedLayoutSegment is usually called via a Client Component that is imported into a Layout.
No. The (..) convention is based on route segments, not the file-system. It does not consider @slot folders in Parallel Routes.
Use (.) to match segments at the same level. For example, (.)photo intercepts the photo segment at the current level.
Pass the slot name as the parallelRoutesKey parameter. For example, useSelectedLayoutSegment('auth') returns the active segment within the @auth slot.
Yes. app/shop/[[...slug]]/page.js will match /shop, /shop/a, /shop/a/b, etc. With optional catch-all, the route without the parameter is also matched.
Returns undefined. At /shop with [[...slug]], params become { slug: undefined }.
Yes. Parallel routes can be streamed independently, allowing you to define independent error and loading states for each route. Each slot can have its own loading.tsx and error.tsx files.
Metadata and SEO > Basic SEO Metadata
41 questionsAdd a number suffix to the file name (e.g., icon1.png, icon2.png). Numbered files will sort lexically.
Only layout.js, layout.tsx, page.js, and page.tsx files can export metadata objects or generateMetadata functions.
title.absolute ignores title.template set in parent segments, allowing you to provide a title that completely overrides the template.
An array of strings (string[]), where each author name generates a separate tag.
File-based metadata has higher priority and will override config-based metadata (metadata object or generateMetadata function).
Advanced layouts like display: grid will not work. ImageResponse only supports flexbox and limited CSS properties.
sizes="any" is added when the extension is .svg or the image size cannot be determined.
Nested fields like openGraph and robots defined in parent segments are completely overwritten by the child segment, not merged, unless explicitly spread using the spread operator.
No. You cannot export both the metadata object and generateMetadata function from the same route segment.
They are Promises that must be awaited. For example: const { id } = await params
No. title.template applies to child route segments only, not the segment it's defined in.
Metadata objects are shallowly merged together. Duplicate keys are replaced based on their ordering, with child values overwriting parent values.
The type is named 'Metadata' and is imported via: import type { Metadata } from 'next'
The default is 'index,follow' (or 'all' as an alternative), meaning pages can be indexed and links can be followed.
viewport, colorScheme, and themeColor were deprecated. Use generateViewport instead.
'always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', or 'never'
The %s placeholder is used and will be replaced with the specific page title.
No. You cannot generate a favicon icon. Only static .ico files are supported for favicon.
No. The metadata object and generateMetadata function exports are only supported in Server Components.
No. searchParams is only available in page.js files, not in layout.js files. Layouts don't receive searchParams because they don't re-render during navigation.
Either a relative path (combined with metadataBase) or an absolute URL string.
No. Streaming metadata is disabled for bots and crawlers that expect metadata in the
tag.Yes. robots.js is a special Route Handler that is cached by default unless it uses a Dynamic API or dynamic config option.
It will cause a build error. metadataBase must be explicitly set to use relative URLs in URL-based metadata fields.
rules (object or array with userAgent, allow, disallow, crawlDelay), sitemap (string or string array), and host (string).
Caching and Revalidating > Revalidation Methods
41 questionsThe single-argument form revalidateTag(tag) is deprecated and immediately expires the tag entry, causing a blocking revalidate on the next request. This form may be removed in future versions.
The lowest revalidate value across each layout and page of a single route will determine the revalidation frequency of the entire route.
You must enable the cacheComponents flag: const nextConfig = { cacheComponents: true }
The whole route revalidation interval will be decreased to match the lower value.
updateTag immediately expires the cached data and the next request will wait to fetch fresh data. revalidateTag (with profile="max") can serve stale content while revalidating in the background.
The type parameter is required when path contains dynamic segments (e.g., '/product/[slug]'). If path refers to a literal route segment (e.g., '/product/1'), you should not provide type.
updateTag can only be called in Server Actions. It cannot be used in Route Handlers, Client Components, or other contexts.
It caches the resource indefinitely, semantically equivalent to revalidate: Infinity. The HTTP cache may still evict older resources over time.
Routes with any fetch having revalidate: 0 or no-store become dynamically rendered.
Arguments to cached functions and their return values must be serializable.
fetch('https://...', { next: { revalidate: 3600 } }) where 3600 is the number of seconds.
revalidateTag can be called in Server Functions and Route Handlers. It cannot be called in Client Components or Proxy environments.
Stale time (client): 5 minutes, Revalidate time (server): 15 minutes, Expiration: Never expires by time.
Both options will be ignored, and in development mode a warning will be printed to the terminal.
Yes, cache tags are case-sensitive for both revalidateTag, updateTag, and cacheTag.
revalidatePath can be called in Server Functions and Route Handlers. It cannot be called in Client Components or Proxy environments.
It uses the default heuristic to cache any fetch requests that set their cache option to 'force-cache' or are discovered before a Dynamic API is used. Semantically equivalent to revalidate: Infinity, meaning the resource should be cached indefinitely.
No, the revalidate export is unavailable when using runtime = 'edge'.
If revalidation fails, the last successfully generated data continues serving from cache.
It ensures a layout or page is always dynamically rendered even if no Dynamic APIs or uncached data fetches are discovered. This changes the default of fetch requests that do not set a cache option to 'no-store'.
profile="max" is recommended. It marks the tag entry as stale and uses stale-while-revalidate semantics on the next visit.
- Using fetch with next.tags option: fetch('...', { next: { tags: ['user'] } }), and 2. Using cacheTag() function within cache components using 'use cache' directive.
cacheTag accepts one or more string values as parameters, for example: cacheTag('tag-one', 'tag-two')
The layout file, all nested layouts, and all nested pages are invalidated.
After the revalidate period expires, the next request receives the stale cached page, while Next.js regenerates a fresh version in the background. Once successful, the updated page replaces the cached version.
In Server Functions, it updates the UI immediately (if viewing the affected path) with a temporary side effect of refreshing all previously visited pages. In Route Handlers, it marks the path for revalidation on the next visit, and dynamic route segments won't trigger multiple simultaneous revalidations.
Upgrading > Build System & Bundler Updates
41 questionsTurbopack does not support the legacy tilde () prefix for node_modules imports. You must either remove the tilde (e.g., change `@import 'bootstrap/...'to@import 'bootstrap/...') or use turbopack.resolveAliaswith'~*': '*'` mapping.
bundlePagesExternals (previously named experimental.bundlePagesExternals) - it was renamed in v15.0.0 when it moved from experimental to stable.
Turbopack for production builds was not stable in Next.js 15.0. It progressed from alpha in 15.3 to beta in 15.5.
No, it is currently experimental and subject to change; production use is not recommended.
Use the expanded format with a loader object: loaders: [{ loader: '@svgr/webpack', options: { icon: true } }]
It overwrites the original resolve extensions with the provided list. Example: resolveExtensions: ['.mdx', '.tsx', '.ts', '.jsx', '.js', '.mjs', '.json']
Global CSS and CSS Modules (via Lightning CSS), modern CSS nesting, @import syntax, PostCSS, and Sass/SCSS (out of box for Next.js).
Run npx @next/codemod@canary upgrade latest to automatically upgrade to the latest Next.js version.
The next dev command outputs to .next/dev instead of .next, enabling concurrent execution of next dev and next build.
The as property specifies what file type the loader output should be treated as. Example: { '*.svg': { loaders: ['@svgr/webpack'], as: '*.js' } } treats SVG files as JavaScript after transformation.
Run NEXT_TURBOPACK_TRACING=1 next dev which produces a .next/dev/trace-turbopack file.
Turbopack for development (next dev --turbo) was stable and production-ready in Next.js 15.
Only loaders that return JavaScript code are supported. Loaders that transform files like stylesheets or images are not currently supported.
No, Turbopack does not support webpack plugins. However, it does support webpack loaders (with a subset of the webpack loader API).
npx next internal trace .next/dev/trace-turbopack (note the .next/dev path, not .next).
It is enabled by default (true) as of v16.1.0 and is marked as stable.
Next.js uses a custom SWC optimization that only applies the React Compiler to relevant files (like those with JSX or React Hooks), avoiding unnecessary work and leading to faster builds.
Up to 10× faster Fast Refresh and 2–5× faster production builds compared to webpack.
Turbopack follows JavaScript import order to order CSS modules. This can lead to subtle rendering changes when adopting Turbopack if applications relied on webpack's arbitrary ordering.
Options passed to webpack loaders must be plain JavaScript primitives, objects, and arrays.
Files outside of the project root are not resolved by default. For linked dependencies (via npm link, yarn link, pnpm link), you must configure turbopack.root to the parent directory of both the project and linked dependencies.
Type: Array of strings (package names). Default behavior: Next.js automatically opts out a curated list of 80+ popular packages known to have Node.js-specific dependencies (database clients, build tools, testing frameworks, etc.).
@mdx-js/loader, @svgr/webpack, and babel-loader are among the verified working loaders. babel-loader is configured automatically if a Babel configuration file is found.
serverExternalPackages - it allows you to opt-out specific packages from automatic server-side bundling.
root (sets application root directory), rules (defines webpack loaders for file transformations), resolveAlias (manual module name mappings), and resolveExtensions (modifies file extensions for module resolution).
Yarn PnP, experimental.urlImports, experimental.esmExternals, nextScriptWorkers, sri.algorithm, and fallbackNodePolyfills are unsupported.
Globs match based on file name unless the glob contains a / character, which causes it to match based on the full project-relative file path.
More than 50% of development sessions and 20% of production builds on Next.js 15.3+ were running on Turbopack.
npx @next/codemod@canary next-lint-to-eslint-cli . - this is necessary because the next lint command has been removed entirely in Next.js 16.
"dev": "next dev", "build": "next build", "start": "next start", "lint": "eslint", "lint:fix": "eslint --fix". No --turbopack flag is needed since it's the default.
It maps package imports to alternative destinations, similar to webpack's resolve.alias. Supports conditional aliasing with conditions like browser. Example: resolveAlias: { underscore: 'lodash', mocha: { browser: 'mocha/browser-entry.js' } }
No, custom Sass functions via sassOptions.functions are not supported due to Turbopack's Rust architecture.
Available conditions include: browser, foreign, development, production, node, and edge-light.
Expect compile times in development and during builds to be higher when enabling this option as the React Compiler relies on Babel.
npx next internal trace [path-to-file] - then view the trace at https://trace.nextjs.org/
It is not enabled by default and remains marked as experimental (opt-in).
The foreign condition matches code in node_modules and some Next.js internals. Usually you'll want to restrict loaders to {not: 'foreign'} to improve performance.
Install babel-plugin-react-compiler with npm install -D babel-plugin-react-compiler, then set reactCompiler: true in next.config.js (no longer under experimental).
experimental.turbo (used in Next.js versions 13.0.0 to 15.2.x). The experimental.turbo option will be removed in Next.js 16.
Route Handlers > Advanced Integration Patterns
40 questionsNo, starting with Next.js 16, synchronous access is fully removed. These APIs can only be accessed asynchronously
No, the Router Cache will continue to serve the previous payload until a hard refresh, as the Route Handler isn't tied to a specific route
No, you can only delete cookies from the same domain. The delete method is only callable in Server Actions or Route Handlers
redirect() sends the user to a different URL (changes browser URL), while rewrite() proxies the request to a different URL while preserving the original browser URL
No, eval(), new Function(), WebAssembly.compile(), and WebAssembly.instantiate() are not supported in Edge Runtime
No, you do not need to use bodyParser or any additional configuration. Route Handlers use standard Web Request API methods like request.json(), request.formData(), and request.text()
Seconds (it's a number representing the maximum allowed duration for the function to execute in seconds)
No, you cannot set cookies after streaming initiates. You must set cookies before streaming begins
'auto' | 'default-cache' | 'only-cache' | 'force-cache' | 'force-no-store' | 'default-no-store' | 'only-no-store'
Yes, using these functions automatically opts routes into dynamic rendering, as values cannot be determined ahead of time
No, headers() returns a read-only Web Headers object. To set headers, you need to return a new Response with new headers
It can take hundreds of milliseconds for Serverless Functions to boot up before they begin processing requests
Next.js will automatically implement OPTIONS and set the appropriate Response Allow header depending on the other methods defined in the Route Handler
The Vercel AI SDK with StreamingTextResponse and streamText from @ai-sdk/openai, though you can also use the native Web API ReadableStream
Use NextResponse.next({ request: { headers: modifiedHeaders } }). Note: use the request object wrapper, not just { headers: modifiedHeaders }
GET methods are not cached by default in Next.js 15. This changed from static to dynamic in v15.0.0-RC.
headers() is an async function that returns a promise. You must use await or React's use() function
Use Proxy (middleware) or the next.config.js file instead of adding CORS headers to each individual Route Handler
Yes, they are cached by default unless they use a Dynamic API or dynamic config option
sitemap.xml, robots.txt, app icons, and open graph images all have built-in support
name, value, expires (Date), maxAge (Number in seconds), domain, path (default '/'), secure (Boolean), httpOnly (Boolean), sameSite (Boolean | 'lax' | 'strict' | 'none'), priority ('low' | 'medium' | 'high'), partitioned (Boolean)
params is now a Promise that must be awaited. Example: const params = await segmentData.params
Only paths provided by generateStaticParams will be served, and unspecified routes will return 404 or match (in the case of catch-all routes)
Edge Runtime has 'Low' cold boot times with 'Lowest' latency, while Node.js Runtime has 'Normal' latency with no specific cold boot concerns mentioned
To override the default cache option behavior for fetch() requests in the Route Handler
cookies() is an async function that returns a promise. You must use await or React's use() function: const cookieStore = await cookies()
Use request.nextUrl.searchParams. Example: const searchParams = request.nextUrl.searchParams; const query = searchParams.get('query')
Yes, Route Handlers will render a static response when running next build. Only the GET HTTP verb is supported for static generation
Rendering Strategies > Dynamic Content Handling
39 questionstrue. When dynamicParams = true, dynamic segments not included in generateStaticParams are generated on demand using Streaming Server Rendering.
Only paths provided by generateStaticParams will be served, and unspecified routes will return a 404 (or match in the case of catch-all routes).
Setting dynamic = 'force-dynamic' is functionally equivalent to setting all fetch requests to { cache: 'no-store', next: { revalidate: 0 } }.
Yes. A codemod is available to automate the process: npx @next/codemod@canary next-async-request-api .
The use cache directive marks a route, React component, or function as cacheable. It can be used at the top of a file to indicate all exports should be cached, or inline at the top of a function/component to cache the return value. It provides longer-term caching across multiple requests with automatic cache key generation.
React cache provides request-level memoization within a single render pass, allowing you to call the same function multiple times while only executing it once.
Setting revalidate = 0 ensures a layout or page is always dynamically rendered, even if no dynamic functions or uncached data fetches are discovered.
connection() replaced unstable_noStore in Next.js 15.0.0. As of v15.0.0, unstable_noStore is deprecated and is now a legacy API.
The four values are: 'auto' (default), 'force-dynamic', 'error', and 'force-static'.
draftMode() transitioned to an asynchronous function in v15.0.0-RC. It was originally introduced as synchronous in v13.4.0.
No. Wrapping a component in Suspense doesn't make the component itself dynamic, but rather Suspense is used as a boundary. Components become dynamic when using specific APIs like cookies, headers, connection, draftMode, searchParams, etc.
Setting dynamic = 'error' or dynamic = 'force-static' changes the default of dynamicParams from true to false.
A new bypass cookie value will be generated each time you run next build. This ensures that the bypass cookie can't be guessed.
An array of objects where each object represents the populated dynamic segments of a single route. Property names correspond to segment names, with values filling those segments.
Setting next.revalidate to false caches the resource indefinitely (equivalent to Infinity).
PPR is experimental and subject to change; it is not recommended for production as of Next.js 15.5.8.
Temporarily yes, for backward compatibility. The APIs can be accessed synchronously but will show warnings in development and production. However, starting with Next.js 16, synchronous access is fully removed.
cookies, headers, connection, draftMode, searchParams, unstable_noStore, and fetch with { cache: 'no-store' } will all opt the whole route into dynamic rendering at request time.
Next.js fetches the resource from the remote server on every request, even if Dynamic APIs are not detected on the route. This opts out of the Data Cache entirely.
No. cookies.set() and cookies.delete() only work in Server Actions or Route Handlers, not in Server Components, because cookies are stored by the browser, not the server.
The lowest revalidate value across each layout and page of a single route will determine the revalidation frequency of the entire route.
Duplicate keys become arrays. For example, /shop?a=1&a=2 resolves to Promise<{ a: ['1', '2'] }>.
Yes. Fetch requests are automatically memoized for the same data across all generate-prefixed functions, Layouts, Pages, and Server Components.
loading.js automatically wraps the page.js file and child components in a
During next dev, it runs when navigating to a route. During next build, it executes before corresponding Layouts or Pages are generated. During revalidation (ISR), it does not run again.
connection() returns a Promise
During production builds, the build will fail with a 'Missing Suspense boundary with useSearchParams' error. The Client Component using useSearchParams must be wrapped in a
Using cookies() opts the route out of the Full Route Cache and triggers dynamic rendering at request time, but the route can still use the Data Cache for data requests.
Three capabilities: isEnabled (boolean indicating Draft Mode status), enable() (activates Draft Mode via __prerender_bypass cookie), and disable() (deactivates Draft Mode by removing the cookie).
In development, Next.js fetches the resource from the remote server on every request. During next build, it fetches once because static prerendering occurs. Dynamic APIs trigger fresh fetches per request.
Some browsers buffer responses until they exceed 1024 bytes, potentially delaying visible feedback in minimal applications.
Promise<{ [key: string]: string | string[] | undefined }>. It became a promise in v15.0.0-RC, whereas in version 14 and earlier it was synchronous.
connection() was introduced in v15.0.0-RC and became stabilized in v15.0.0.
Conflicting options are disallowed. You cannot combine revalidate with cache: 'no-store'.
headers() returns a read-only Web Headers object with methods including get(), has(), entries(), keys(), values(), and forEach().
cookies() became async in Next.js v15.0.0-RC. In version 14 and earlier, it was synchronous.
Using Dynamic APIs like cookies, headers, connection, draftMode, searchParams, unstable_noStore, or fetch with cache: 'no-store' throws a React error during build unless wrapped in Suspense.
Fetch requests are automatically memoized in Next.js, so you don't need to wrap them in React cache. Use React cache to manually memoize data requests when fetch is not suitable (e.g., database clients, CMS clients, or GraphQL clients).
connection() is only necessary when dynamic rendering is required and common Dynamic APIs (cookies, headers, searchParams, etc.) are not used, such as when accessing values like Math.random() or new Date() that should produce different results per request.
Project Structure > Routing Conventions
39 questionsYes, on navigation, layouts preserve state, remain interactive, and do not rerender.
Yes, a page is always the leaf of the route subtree, meaning it cannot have child routes.
Place the file in the root of your project, or inside a src folder if using one, at the same level as pages or app.
No, slots are not route segments and do not affect the URL structure. For example, for /@analytics/views, the URL will be /views since @analytics is a slot.
Prefix the folder with an underscore: _folderName. This opts the folder and all its subfolders out of routing.
When you navigate between routes that use different root layouts, it triggers a full page reload.
Wrap the folder name in parentheses: (folderName). This indicates the folder is for organizational purposes and should not be included in the route's URL path.
default.js is rendered when Next.js cannot recover a slot's active state after a full-page load.
Prefix the folder name with %5F (the URL-encoded form of an underscore): %5FfolderName.
Create a middleware.ts (or .js) file in the project root, or inside src if applicable, at the same level as pages or app.
Yes, shared layouts remain interactive while new route segments load.
Use [[...segment]] for optional catch-all segments. This will match the base route /shop, in addition to /shop/clothes, /shop/clothes/tops, etc.
No, templates are given a unique key, meaning children Client Components reset their state on navigation.
No, you cannot generate a favicon icon. Use icon or a favicon.ico file instead.
By default, Next.js accepts files with the following extensions: .tsx, .ts, .jsx, .js.
No, an error.js boundary will not handle errors thrown in a layout.js component in the same segment because the error boundary is nested inside that layout's component.
Slots are defined with the @folder convention (e.g., @analytics, @team). Slots are passed as props to the shared parent layout.
not-found.js returns a 200 HTTP status code for streamed responses, and 404 for non-streamed responses.
Yes, starting in Next.js v15.0.0-RC, params and searchParams are promises that must be awaited using async/await or React's use() function.
Yes, OPTIONS is automatically implemented if not explicitly defined, with Next.js setting the appropriate Response Allow header depending on the other methods defined.
Pages are Server Components by default, but can be set to a Client Component.
You export a register function in the file, which will be called once when a new Next.js server instance is initiated. The register function can be an async function.
error.js receives two props: error (an Error object instance with optional digest property) and reset() (a function to attempt recovery by re-rendering the segment).
Yes, error.js must be a Client Component (must use the 'use client' directive). Error boundaries must be Client Components.
The default caching for GET handlers was changed from static to dynamic in v15.0.0-RC.
Project Structure > Advanced Routing Patterns
39 questionsSlots are passed as props to the shared parent layout. For example, slots @analytics and @team are passed as analytics and team props (without the @ prefix) alongside the children prop.
Prefix the folder name with %5F (the URL-encoded form of an underscore): %5FfolderName.
[[...folderName]] (double square brackets). For example, app/shop/[[...slug]]/page.js will match /shop as well as /shop/clothes, /shop/clothes/tops, etc.
No. Slots are not route segments and do not affect the URL structure. A file at /@analytics/views renders at the /views URL path only—the slot name doesn't appear in the browser address bar.
Yes. Fetch requests are automatically memoized for the same data across all generate-prefixed functions, Layouts, Pages, and Server Components.
The default value is false, which caches indefinitely (equivalent to Infinity).
Hard navigation bypasses interception and renders the full route instead. Only soft navigation (client-side) triggers the interception behavior.
Return null. For example, inside the @auth slot for a modal pattern, add a default.js file that returns null to ensure the modal is not rendered when it's not active.
Yes, when using parallel routes. Since children is an implicit slot, you also need to create a default.js file to render a fallback for children when Next.js cannot recover the active state of the parent page.
Wrap a folder name in parentheses: (folderName). The folder is for organizational purposes and is not included in the route's URL path.
Yes. Only the content returned by page.js or route.js is sent to the client. A route becomes public when a page or route file exists.
The params object contains properties matching each segment name. For example: params: Promise<{ category: string; product: string }>. Each property name is the segment's name, and the value is what the segment is filled in with.
During next build, generateStaticParams runs before the corresponding Layouts or Pages are generated.
Prefix a folder with an underscore: _folderName. This indicates the folder is a private implementation detail and should not be considered by the routing system, opting the folder and all its subfolders out of routing.
No. During revalidation (ISR), generateStaticParams will not be called again.
It accepts a parallelRoutesKey parameter. When a user navigates to a route, the function returns the active route segment name within that specific slot (e.g., the string 'login' for the login segment).
Route segments. The (..) convention is based on route segments, not the file-system. For example, it does not consider @slot folders in Parallel Routes.
Navigating across multiple root layouts will cause a full page load (as opposed to a client-side navigation). This only applies to multiple root layouts.
The value is undefined when accessing the base route (e.g., /shop for app/shop/[[...slug]]/page.js).
Yes. Parallel Routes can be streamed independently, allowing you to define independent error and loading states for each route as they're being streamed in independently.
The revalidate value needs to be statically analyzable. For example, revalidate = 600 is valid, but revalidate = 60 * 10 is not.
During hard navigation (full-page load) when Next.js cannot recover a slot's active state. It renders as a fallback for unmatched slots during the initial load or full-page reload.
The child generateStaticParams function is executed once for each segment a parent generateStaticParams generates.
string[] | undefined. It's an array of strings for nested paths, or undefined for the base route.
Only paths provided by generateStaticParams will be served, and unspecified routes will return 404 or match (in the case of catch-all routes).
string[] (an array of strings). For example, /shop/clothes/tops would have params.slug = ['clothes', 'tops'].
No. Routes in different groups should not resolve to the same URL path. For example, (marketing)/about/page.js and (shop)/about/page.js both resolving to /about will cause an error.
A Promise. You must use async/await or React's use hook to access the values. For example: params: Promise<{ slug: string }>.
A 404 is rendered for named slots. The documentation states: 'If default.js doesn't exist, an error is returned for named slots...and requires you to define a default.js'.
The default value is 'auto'. It inherits from the parent layout if unspecified.
Yes, but with different capabilities. Pages can generate params for current and parent segments. Layouts can only generate params for their own segment, not children.
During development, the function executes when you navigate to a route.
The default value is true, which means dynamic segments not included in generateStaticParams are generated on demand.
With optional catch-all [[...slug]], the route without the parameter is also matched (e.g., /shop). Required catch-all [...slug] requires at least one segment.
Yes. Project files can be safely colocated inside route segments in the app directory without accidentally being routable, because only content returned by page.js or route.js gets sent to the client.
No. If one slot is dynamic, all slots at that level must be dynamic. You cannot have separate static and dynamic slots at the same route segment level.
Image Optimization > Caching and Delivery
39 questionsSharp - it is significantly faster than Squoosh and uses less memory. It's a fast and efficient image optimization Node.js module using the native libvips library.
Optimized images are cached in the
Yes, each part of the src value is strictly matched against your remotePatterns definitions with exact and case-sensitive matching.
Next.js automatically hashes file contents and caches static image imports forever with a Cache-Control header of immutable.
false - SVG optimization is disabled by default for security reasons.
s-maxage is preferred over max-age when both are present in the Cache-Control header.
Squoosh - a fully node-based image optimization solution that is slower but doesn't require additional libraries.
You must set images.unoptimized to true in next.config.js, as the Image Optimization API requires a server and cannot function in purely static exports.
Next.js adds a tag in the document head, instructing the browser to load the image as soon as possible.
Set contentDispositionType to 'attachment' and add contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;" to prevent malicious scripts embedded in SVG files from executing.
position: absolute (by default), and the parent element must have position: relative, fixed, or absolute.
No, the cache directory is hard-coded as
lazy - images are deferred until they reach a calculated distance from the viewport. This is native browser lazy loading.
'attachment' - which forces the browser to download the image when visiting directly, rather than rendering it inline.
You can set loader to 'cloudinary', 'imgix', or 'akamai' for built-in support, or 'custom' with a loaderFile for custom implementations.
No, there is no built-in cache invalidation mechanism. You must either keep minimumCacheTTL low, manually change the src prop, or delete cached files from
Next.js automatically detects the browser's supported image formats via the request's Accept header to determine the optimal output format.
You must configure the Proxy to forward the Accept header, as Next.js uses it to determine the best image format for each browser.
A very small image (10px or less) is recommended, as large blurDataURL values may hurt performance.
You must add the Accept header to the cache key to ensure the CDN caches images in all formats (WebP, AVIF, etc.), not just the first format it receives.
No, they cannot be used together. The unoptimized prop serves images as-is with no loader transformation.
Squoosh can cause very high memory usage during image optimizations and might crash the entire Next.js container in a limited resource environment.
MISS (not in cache, occurs on first visit), STALE (in cache but exceeded revalidate time, will be updated in background), and HIT (in cache and has not exceeded revalidate time).
The browser assumes the image will be as wide as the viewport (100vw), which can cause unnecessarily large images to be downloaded.
'async' (default - asynchronously decode), 'sync' (synchronously decode), or 'auto' (browser chooses).
['image/webp'] - WebP is the default format if you don't specify your own.
The expiration is defined by either the minimumCacheTTL configuration or the upstream image Cache-Control header, whichever is larger.
The priority property has been deprecated in favor of the preload property. In most cases, you should use loading="eager" or fetchPriority="high" instead of preload.
protocol, hostname, port, pathname, and search. When omitting any of these, the wildcard ** is implied, which is not recommended for security.
src (the image source), width (requested image width), and quality (image quality, defaults to 75).
Without sizes, Next.js generates a limited srcset (e.g. 1x, 2x) for fixed-size images. With sizes, it generates a full srcset (e.g. 640w, 750w, etc.) optimized for responsive layouts.
Upgrading > Upgrade Methods & Tooling
39 questionsTransforms Route Segment Config runtime value experimental-edge to edge
They do not recommend serving production traffic from canary versions
Next.js will error in both dev and build to enforce that you address the issues
- Make the necessary changes and remove the comment, or 2) Replace @next-codemod-error with @next-codemod-ignore to bypass the build error
It either wraps them with await (for Server Components) or React.use() (for Client Components)
--dry (performs a dry-run without editing code), --print (displays changed output for comparison), and --verbose (shows detailed output during the upgrade process)
Configuration > Performance & Optimization
39 questionsNext.js uses gzip compression by default when using next start or a custom server.
reactStrictMode is true by default with App Router since Next.js 13.5.1.
The default image optimization loader will follow HTTP redirects up to 3 times.
The default minimumCacheTTL is 14400 seconds (4 hours) in Next.js 16. This changed from the previous default of 60 seconds.
The default value is 'attachment', which forces the browser to download the image when visiting directly.
The default value is false, which means URLs with trailing slashes are redirected to URLs without trailing slashes (e.g., /about/ redirects to /about).
The configuration has been moved to the top-level cacheHandler property. Use cacheHandler instead of experimental.incrementalCacheHandlerPath.
No, the minimal server in standalone mode does not copy the public or .next/static folders by default. These folders should ideally be handled by a CDN, though they can be copied manually to standalone/public and standalone/.next/static.
No, source maps are disabled by default in production builds. You must set productionBrowserSourceMaps: true to enable them.
The stable replacement is serverExternalPackages. The experimental.serverComponentsExternalPackages option was renamed to serverExternalPackages in Next.js 15.
No, removeConsole is not enabled by default. You must explicitly configure it in next.config.js.
27 packages/patterns are optimized by default, including lucide-react, date-fns, lodash-es, ramda, antd, react-bootstrap, ahooks, @ant-design/icons, @headlessui/react, @headlessui-float/react, @heroicons/react/20/solid, @heroicons/react/24/solid, @heroicons/react/24/outline, @visx/visx, @tremor/react, rxjs, @mui/material, @mui/icons-material, recharts, react-use, @material-ui/core, @material-ui/icons, @tabler/icons-react, mui-core, react-icons/, effect, and @effect/.
Yes, build workers are enabled by default unless you have a custom webpack config.
No, fully static pages are not affected by outputFileTracing. The standalone mode traces server-rendered routes only.
Yes, starting with Next.js 16, the qualities field is required because unrestricted access could allow malicious actors to optimize more qualities than intended.
The default value is true, meaning Next.js will add the X-Powered-By header by default.
Three HTML files are outputted to
The SWC compiler has been enabled by default since Next.js version 12.
The ANALYZE environment variable is conventionally used, typically configured as enabled: process.env.ANALYZE === 'true'.
No, reactRemoveProperties is not enabled by default. It requires explicit configuration in next.config.js.
No, for security reasons, the Image Optimization API using the default loader will not forward headers when fetching the src image.
The default runtime is 'nodejs'. If the segment runtime is not set, the default nodejs runtime will be used.
No, the appDir experimental option is no longer needed as of Next.js 13.4, as the App Router is now stable. You can simply create an app/ directory without any configuration.
Caching and Revalidating > Cache Interactions
39 questionsdynamic='error' forces static rendering and is equivalent to getStaticProps. It sets all fetches to cache: 'force-cache', enabling Data Cache and Full Route Cache while preventing dynamic APIs.
next.revalidate: false caches indefinitely and is semantically equivalent to revalidate: Infinity.
Combining 'only-cache' with 'only-no-store' or 'force-cache' with 'force-no-store' is forbidden within a single route.
If a route has a fetch request that is not cached (using cache: 'no-store'), this will opt the route out of the Full Route Cache. Other fetch requests that explicitly enable caching will still be cached in the Data Cache.
Conflicting options like { revalidate: 3600, cache: 'no-store' } are disallowed and ignored.
Revalidating or opting out of the Data Cache will invalidate the Full Route Cache, as the render output depends on data.
The recommended value is 'max', which provides stale-while-revalidate semantics (serving stale content while fetching fresh content in the background).
dynamic='force-static' forces static rendering while making cookies(), headers(), and useSearchParams() return empty values.
The tag parameter in revalidateTag has a maximum of 256 characters and is case-sensitive.
Without the profile parameter (deprecated), the tag entry is expired immediately, and the next request to that resource will be a blocking revalidate/cache miss.
revalidate=0 means the route is always dynamically rendered even without Dynamic APIs, and changes the default fetch behavior to 'no-store' unless explicitly set otherwise.
Next.js looks for a matching request in its Data Cache. If there is a match and it is fresh, it will be returned from the cache. If there is no match or a stale match, Next.js will fetch the resource from the remote server and update the cache.
The type parameter is required when the path contains dynamic segments (e.g., '/product/[slug]'). If path is a specific URL (e.g., '/product/1'), the type parameter should be omitted.
The path parameter in revalidatePath has a maximum of 1024 characters.
router.refresh() clears only the Router Cache and re-renders the current route on the server without affecting the Data Cache or Full Route Cache. revalidatePath() purges both the Data Cache and Full Route Cache, triggering fresh data fetching and server-side re-rendering.
The default value is 'auto', which caches fetches before Dynamic APIs and skips caching after.
If two fetch requests with the same URL in the same route have different revalidate values, the lower value will be used.
The default value is 'auto', which caches as much as possible without preventing any components from opting into dynamic behavior.
The default is 5 minutes (300 seconds) for statically generated pages or when prefetch={true} is set.
In Server Functions, revalidatePath updates the UI immediately (if viewing the affected path). In Route Handlers, it marks the path for revalidation that happens on the next visit to the specified path.
fetch responses are not cached by default, but Next.js will pre-render routes containing fetch requests and cache the HTML output.
Revalidating the Data Cache in a Route Handler will not immediately invalidate the Router Cache since the handler isn't tied to a specific route.
The default is 0 seconds (no caching) in v15.0.0+. The previous default in v14.2.0 was 30 seconds.
Invalidating or opting out of the Full Route Cache does not affect the Data Cache.
No, router.refresh() could reproduce the same result if fetch requests are cached, as it respects existing cache headers.
Next.js fetches the resource from the remote server on every request, even if Dynamic APIs are not detected on the route.
revalidate=0 skips both the Full Route Cache and Data Cache, causing components to render and data to fetch on every incoming server request. The Router Cache (client-side) still applies.
router.refresh() preserves unaffected client-side React state (e.g., useState) and browser state (e.g., scroll position).
Use { expire: 0 } as the profile parameter to enable immediate expiration for webhook scenarios requiring instant data refresh.
The default value is false, which caches indefinitely via heuristic caching. Individual fetches can override with cache: 'no-store'.
Using dynamic='force-dynamic' or revalidate=0 route segment config options will skip the Full Route Cache and the Data Cache.
The lowest revalidate across each layout and page of a single route will determine the revalidation frequency of the entire route.
No, Request Memoization doesn't apply to fetch requests in Route Handlers as they are not a part of the React component tree. It only applies to fetch requests in generateMetadata, generateStaticParams, Layouts, Pages, and other Server Components.
dynamic='force-dynamic' is equivalent to setting every fetch() request in a layout or page to { cache: 'no-store', next: { revalidate: 0 } }.
5 minutes for both static and dynamic pages when using full prefetching (prefetch={true} or router.prefetch).
Request memoization persists only the lifetime of a server request until the React component tree has finished rendering and is not shared across different requests.
When type='layout', revalidatePath invalidates the layout file at that segment, all nested layouts beneath it, and all pages beneath them.
No, revalidatePath('/blog/[slug]') won't invalidate nested routes like '/blog/[slug]/[author]'. The scope is limited to the specific path pattern provided.
Image Optimization > Performance Optimization
38 questionsYes, the next/image component uses browser native lazy loading, which may fallback to eager loading for older browsers before Safari 15.4.
The width and height properties represent the intrinsic image size in pixels. They do not determine the rendered size of the image, which is controlled by CSS.
Preloading images in the initial viewport has shown improvements to the Largest Contentful Paint by up to 50%.
No, there is no mechanism to invalidate the cache at this time, so it's best to keep minimumCacheTTL low. You may need to manually change the src prop or delete files in
You can install the wasm variant by running npm install --cpu=wasm32 sharp.
It allows you to override the automatically generated src attribute of the resulting element, useful for maintaining the same src attribute for SEO purposes when upgrading from
to
A reference to the underlying element with properties like naturalWidth accessible (e.g., onLoadingComplete={(img) => console.log(img.naturalWidth)}).
onLoadingComplete is deprecated in Next.js 14; developers should use onLoad instead.
[75] is the only allowed quality by default in Next.js 16. The qualities field is required because unrestricted access could allow malicious actors to optimize more qualities than intended.
styled-jsx should not be used. Instead, use className (with CSS Module or global stylesheet) or the style prop.
Set output: 'export' in next.config.js and either set images: { unoptimized: true } or define a custom image loader.
Omitting these values implies the wildcard **, which is not recommended because it may allow malicious actors to optimize urls you did not intend.
"attachment" is the default, which forces the browser to download the image when visiting directly for added security.
sharp is strongly recommended for production environments (using next start), while squoosh is used as the default in development.
preload={false} by default; set to true only for above-the-fold hero images.
The quality will be coerced to the closest value in images.qualities. For example, given qualities: [25, 50, 75], a quality prop of 80 is coerced to 75.
If using the style prop to set a custom width, be sure to also set height: 'auto' to preserve the image's aspect ratio.
No, the domains configuration does not support wildcard pattern matching and it cannot restrict protocol, port, or pathname. Use remotePatterns instead.
high, low, and auto (the standard HTML values for the fetchpriority attribute).
getImageProps cannot be used with the placeholder prop because the placeholder will never be removed.
100vw (full screen width) is used as the default if sizes is not specified with fill.
Next.js 15 removed squoosh in favor of sharp as an optional dependency. You no longer need to manually install sharp — Next.js will use sharp automatically when using next start or standalone output mode.
No, Next.js automatically determines the intrinsic width and height for statically imported images. Width and height must be set manually for remote images.
imageSizes is only used for images which provide a sizes prop, indicating the image is less than the full width of the screen. The sizes in imageSizes should all be smaller than the smallest size in deviceSizes.
Starting with Next.js 16, the priority property has been deprecated in favor of the preload property. In most cases, use loading="eager" or fetchPriority="high" instead.
"default-src 'self'; script-src 'none'; sandbox;" to prevent scripts embedded in the SVG from executing.
The default is loading="lazy", which defers loading the image until it reaches a calculated distance from the viewport.
You'll see a console warning if the LCP element is an
Data Fetching > Fetching Strategies
38 questionsIf one request fails when using Promise.all, the entire operation will fail.
It automatically wraps the page.js file and any children below in a
Yes, you can combine the cache function, the preload pattern, and the server-only package to create a data fetching utility that guarantees data fetching only happens on the server.
No, conflicting options are not allowed. Both will be ignored, and in development mode a warning will be printed to the terminal.
Promise.allSettled, which waits for all input promises to complete regardless of whether one rejects, allowing you to handle partial failures gracefully.
Yes, both options prevent caching and fetch fresh data on every request. They produce the same behavior.
30 seconds. This minimum is enforced to ensure prefetched links remain functional.
It forces dynamic rendering for each user at request time, equivalent to setting all fetches to { cache: 'no-store', next: { revalidate: 0 } }.
No, pages are not cached by default as of Next.js 15. Layouts and loading states are cached and reused.
fetch responses are not cached by default. However, Next.js will pre-render the route and cache the output for improved performance.
No, they cannot directly access these. They must be read outside cached scopes and passed as arguments.
Node.js runtime (the default). ISR is only supported when using the Node.js runtime.
No, memoization applies within the React Component tree (layouts, pages, server components) but not Route Handlers, as they are not part of the React component tree.
false, which caches indefinitely with individual fetches able to override.
Only the GET method in fetch requests. Other methods, such as POST and DELETE, are not memoized.
No, updateTag() is exclusively for Server Actions and cannot be used in Route Handlers.
SWR (and also mentions TanStack Query as an alternative). SWR is created by the Next.js team and handles caching, revalidation, focus tracking, refetching on intervals, and more.
It allows any cache option to be passed to fetch, but if no option is provided then sets the cache option to 'force-cache'. This means even fetch requests after Dynamic APIs are considered static.
Any fetch requests with await will block rendering and data fetching for the entire tree beneath it, unless wrapped in a
You must set cacheComponents: true in next.config.ts or next.config.js
Session-based: it persists across navigation but clears on page refresh.
It ensures dynamic rendering even without Dynamic APIs and changes the default fetch behavior to 'no-store'.
Class instances and functions (except as pass-through) cannot be serialized.
The lowest revalidate time will be used for the entire route, ensuring that child pages are revalidated as frequently as their parent layouts.
Installation
38 questionsTypeScript version 5.1.0 or higher is required. For async Server Components, TypeScript 5.1.3 or higher is required.
Yes, Turbopack is enabled as the default bundler when using the --yes flag.
typescript.ignoreBuildErrors: true allows production builds despite TypeScript errors.
It is regenerated during next dev, next build, or next typegen commands.
Yes, Git is automatically initialized when you create a new Next.js project with create-next-app.
Node.js v22.10.0 or higher is required for native TypeScript resolver with ESM support.
@types/react 18.2.8 or higher is required for async Server Components.
--import-alias
typedRoutes: true in next.config.ts enables static typing for route links and navigation.
Authentication > Session Management Strategies
38 questionsIt throws an error that renders a Next.js 401 error page for handling authentication failures. Execution stops at the point of invocation.
The proxy.ts or proxy.js file should be at the project root or in the src directory, at the same level as pages or app. If using custom pageExtensions, match that pattern (e.g., proxy.page.ts).
No, HTTP does not allow setting cookies after streaming starts. This is a protocol constraint documented in the Next.js cookies API.
Stateless (cookie-based) sessions are simpler and suit smaller applications with lower server load but may offer less security if not implemented correctly. Database sessions provide better security and scalability for larger, data-sensitive applications but are more complex and use more server resources.
Next.js re-renders the current page and its layouts on the server so the UI reflects the new cookie value.
updateSession() extends the session's expiration time, useful for keeping the user logged in after they access the application again. It retrieves the current session, verifies it, and sets a new expiration time.
The documentation recommends using the HS256 algorithm, implemented as .setProtectedHeader({ alg: 'HS256' }) with SignJWT and algorithms: ['HS256'] with jwtVerify.
Next.js supports two main strategies: (1) Stateless sessions - session data is stored in browser cookies, with the cookie sent on each request for server-side verification; (2) Database sessions - session data resides in a database, with only an encrypted session ID stored in the browser.
The documentation uses SESSION_SECRET as the environment variable name, accessed via process.env.SESSION_SECRET.
Supported options include: name, value, expires (Date), maxAge (number in seconds), domain, path (default '/'), secure (boolean), httpOnly (boolean), sameSite (boolean/'lax'/'strict'/'none'), priority ('low'/'medium'/'high'), and partitioned (boolean).
Wrap verifySession() with React's cache() API to memoize the return value during a React render pass, avoiding redundant verification within the same request.
Both the source and all 'has' items must match AND all 'missing' items must not match for the configuration to be applied.
Jose is compatible with the Edge Runtime, allowing JWT operations to run in edge environments.
Request cookies provide: get(), getAll(), set(), delete(), has(), and clear() methods.
604800 seconds (calculated as 7 × 24 × 60 × 60), which is the equivalent of 7 days.
Next.js recommends iron-session (a low-level, encrypted, and stateless session utility) or Jose (compatible with Edge Runtime) for stateless sessions. For complete authentication solutions, it lists Auth0, Better Auth, Clerk, Descope, Kinde, Logto, NextAuth.js, Ory, Stack Auth, Supabase, Stytch, and WorkOS.
The serverActions.allowedOrigins configuration option specifies a list of safe origins for large applications using reverse proxies or multi-layered backend architectures.
The secret should be encoded using TextEncoder: const encodedKey = new TextEncoder().encode(secretKey).
Proxy defaults to using the Node.js runtime and does not support the runtime config option.
The payload should contain minimal, unique user data such as user ID and role for subsequent requests, but should NOT include personally identifiable information like phone numbers, email addresses, credit card information, or sensitive data like passwords.
It throws an error that renders a Next.js 403 error page, designed for managing authorization failures. Execution stops at the point of invocation.
'/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)' excludes API routes, static files, and metadata from middleware execution.
Create a Data Access Layer (DAL) with a verifySession() function that checks if the session is valid, then redirects or returns user information needed for further requests.
Response cookies provide: get(), getAll(), set(), and delete() methods.
The cookies API provides: get('name') returns object with name and value; getAll() returns array of all matching cookies; has('name') returns boolean; set(name, value, options) sets cookie; delete(name) deletes cookie; toString() returns string representation.
A DAL should: (1) operate exclusively on the server, (2) perform authorization checks, and (3) return safe, minimal Data Transfer Objects (DTOs).
The type field must be either 'header', 'cookie', 'host', or 'query'.
The cookies() API is asynchronous and requires await or React's use() function.
The examples use 7 days ('7d') as the expiration time, implemented with .setExpirationTime('7d') in SignJWT.
Setting large headers can cause a '431 Request Header Fields Too Large' error depending on your backend web server configuration.
Using cookies() is a Dynamic API that triggers dynamic rendering and opts the route out of the Full Route Cache, as its returned values cannot be known ahead of time and depend on runtime request information.
useActionState (which replaced useFormState in React 19) for calling server actions, handling form errors, and displaying pending state.
Version 15.1.0, marked as experimental and not recommended for production use.
Only within the Data Access Layer (DAL) to keep secrets from being exposed to other parts of the application.
The recommended cookie attributes are: httpOnly: true (prevents client-side JavaScript access), secure: true (uses HTTPS), sameSite: 'lax' (controls cross-site requests), expires or maxAge (defines deletion timing), and path: '/' (specifies URL path).
Check if your authentication library supports refresh tokens, which can be used to extend the user's session.
Layouts and Pages > Page and Layout Creation
38 questionsFiles named index automatically route to the root of the directory. For example, pages/index.js → / and pages/blog/index.js → /blog.
No, on navigation, layouts preserve state, remain interactive, and do not rerender.
No, layouts cannot pass data directly to children. This is a fundamental design restriction in Next.js.
Yes, by default, layouts in the folder hierarchy are also nested, which means they wrap child layouts via their children prop.
The params prop is of type Promise<{ [key: string]: any }>. It must be awaited or accessed via React's use() hook.
Navigating between routes that use different root layouts will trigger a full page reload. For example, navigating from /cart that uses app/(shop)/layout.js to /blog that uses app/(marketing)/layout.js causes a full page reload.
The searchParams prop is of type Promise<{ [key: string]: string | string[] | undefined }>.
A Dynamic Segment can be created by wrapping a folder's name in square brackets: [folderName]. For example, app/blog/[slug]/page.js where [slug] is the Dynamic Segment.
Yes, a page.js file is required for the route to be accessible. Only the content inside the page file will be publicly accessible. Without a page.js file, the route segment will return a 404 error.
The difference is that with optional catch-all [[...slug]], the route without the parameter is also matched. For example, [[...slug]] matches both /shop and /shop/clothes, while [...slug] only matches /shop/clothes.
For the prefetch prop set to null or 'auto' (default): For static routes, the full route will be prefetched. For dynamic routes, the partial route down to the nearest segment with a loading.js boundary will be prefetched.
When a route is statically rendered, calling useSearchParams will cause the Client Component tree up to the closest Suspense boundary to be client-side rendered. This allows a part of the route to be statically rendered while the dynamic part that uses useSearchParams is client-side rendered.
Catch-all segments use the [...segmentName] syntax. For example, app/shop/[...slug]/page.js will match /shop/clothes, /shop/clothes/tops, /shop/clothes/tops/t-shirts, etc.
Creating route groups with conflicting paths will cause an error. For example, (marketing)/about/page.js and (shop)/about/page.js would both resolve to /about and cause an error.
Yes, templates create a new instance for each of their children on navigation. Unlike layouts that persist across routes and maintain state, templates are given a unique key and reset their state on navigation.
As of v15.0.0-RC, the params prop transitioned from synchronous to asynchronous, requiring async/await or React's use() function for value access.
Yes, using searchParams will opt the page into dynamic rendering at request time since search parameters are request-time values that cannot be known ahead of time.
No, layouts cannot use getStaticProps or getServerSideProps. This is a fundamental limitation in the Pages Router. You must use client-side fetching with useEffect or libraries like SWR instead.
Yes, during production builds, a static page that calls useSearchParams from a Client Component must be wrapped in a Suspense boundary, otherwise the build fails with the 'Missing Suspense boundary with useSearchParams' error.
Using getInitialProps in _app.js will disable Automatic Static Optimization for pages without getStaticProps. This is not recommended as a pattern.
A route group can be created by wrapping a folder's name in parentheses: (folderName). This indicates the folder is for organizational purposes and should not be included in the route's URL path.
Optional catch-all segments use double square brackets: [[...segmentName]]. For example, app/shop/[[...slug]]/page.js will match /shop in addition to /shop/clothes, /shop/clothes/tops, etc.
The two default meta tags are: (1) the meta charset tag which sets the character encoding for the website, and (2) the meta viewport tag which sets the viewport width and scale for the website to adjust for different devices.
No, folders wrapped in parentheses like (folderName) are excluded from the URL path structure. For example, (marketing)/about/page.js resolves to /about, not /(marketing)/about.
Yes, the root layout is mandatory and must be defined at the app directory level.
Yes, the children prop is an implicit slot that does not need to be mapped to a folder. This means app/page.js is equivalent to app/@children/page.js.
No, you should not manually add
tags such asNext.js 15.5 introduced globally available PageProps, LayoutProps, and RouteContext type helpers with full parameter typing and no imports required.
No, the metadata object and generateMetadata function exports are only supported in Server Components.
Templates are useful when you need to: resynchronize useEffect on navigation, reset the state of child Client Components on navigation, or implement features that rely on useEffect (e.g., logging page views) and useState (e.g., a per-page feedback form).
These type helpers are generated automatically during next dev, next build, or via next typegen.
A page is a React Component exported from a .js, .jsx, .ts, or .tsx file in the pages directory.
Authentication > Core Authentication Concepts
37 questionsget('name') returns a single cookie, getAll() returns array of matching cookies, has('name') checks existence, set(name, value, options) stores a cookie, delete('name') removes a cookie, and toString() converts to string representation.
The minimum, unique user data that'll be used in subsequent requests, such as the user's ID, role, etc. It should not contain personally identifiable information like phone number, email address, credit card information, or sensitive data like passwords.
Authentication verifies if the user is who they say they are. It requires the user to prove their identity with something they have, such as a username and password.
AUTH_SECRET is the only mandatory environment variable for Auth.js, used to encrypt tokens and email verification hashes. It should be a cryptographically secure random string of at least 32 characters.
You can generate it using the Auth.js CLI with 'npx auth secret', or using 'openssl rand -base64 32', or via https://generate-secret.vercel.app/32
Use Node.js runtime by specifying 'export const runtime = "nodejs"' if your authentication requires Node.js-specific APIs. Edge Runtime does not support all Node.js APIs, particularly the native crypto module.
Next.js recommends using 'await bcrypt.hash(password, 10)' with 10 salt rounds to hash passwords before storing them in the database.
For increased security and simplicity, Next.js strongly recommends using authentication libraries rather than custom implementations, noting that custom solutions can quickly become complex and citing increased security and simplicity as key benefits.
HttpOnly (prevents client-side JavaScript access), Secure (requires HTTPS transmission), SameSite (controls cross-site request handling), Max-Age or Expires (specifies cookie deletion timing), and Path (defines the URL path scope).
The sameSite attribute accepts Boolean or String values: 'lax', 'strict', or 'none'.
In Next.js v16.0.0, the middleware file convention was deprecated and renamed to 'proxy'. If using Next.js ≤15, use middleware.ts; for v16+, use proxy.ts (the code itself remains the same, only the filename changes).
Setting and deleting cookies must be performed in a Route Handler or Server Action where the response headers can be properly set. Cookies can be read in Server Components via HTTP request headers.
The main security checks should happen where your app accesses or changes data (the Data Access Layer). While middleware can be useful for initial validation, it should not be the sole line of defense - the bulk of security checks should be performed in the DAL.
The authorized callback is invoked when a user needs authorization, using Middleware/Proxy. It verifies if the request is authorized to access a page. It returns true to allow access or false to redirect to login.
Server Actions execute exclusively on the server, providing a secure environment for handling authentication logic where sensitive operations cannot be accessed or manipulated by the client.
Wrap verifySession with React's cache() function to avoid unnecessary duplicate requests to the database during a render pass: export const getUser = cache(async () => { const session = await verifySession(); ... })
Import the signIn function from your auth.ts config file: import { signIn } from '@/auth';. In Server Actions, call it with: await signIn('credentials', formData);
matcher: '/about' for single path, or matcher: ['/about', '/contact'] for multiple paths. All paths must start with /. Supports modifiers: * (zero or more), ? (zero or one), + (one or more).
Secret keys should be stored in environment variables, but only the Data Access Layer should access process.env. This prevents secrets from spreading throughout the application.
httpOnly restricts the cookie to HTTP requests, preventing client-side JavaScript access, which mitigates the risk of cross-site scripting (XSS) attacks.
The file must export either a default export or a named 'proxy' function. Multiple proxy exports from the same file are not supported.
Catch AuthError instances: try { await signIn('credentials', formData); } catch (error) { if (error instanceof AuthError) { switch (error.type) { case 'CredentialsSignin': return 'Invalid credentials.'; default: return 'Something went wrong.'; } } throw error; }
Next.js references the W3C API minimization principle, which states that APIs should work with the minimum amount of data necessary and should only return data relevant for the specific query, not everything.
Use isLoggedIn = !!auth?.user to verify session exists, and isOnDashboard = nextUrl.pathname.startsWith('/dashboard') to identify protected paths. Return true in authorized callback to allow access; false redirects to login.
The 'has' option specifies conditions based on the presence of specific request elements (headers, query parameters, or cookies), while 'missing' focuses on conditions where certain request elements are absent. Both require a 'type' field (header, cookie, host, or query) and a 'key' field.
Cookies should be set on the server to prevent client-side tampering.
matcher: ['/((?!api|_next/static|_next/image|..png$).)'] - matches all request paths except those starting with api, _next/static, _next/image, and files ending in .png
useActionState exposes a pending boolean that can be used to show a loading indicator or disable the submit button while the action is being executed, and handles form errors and the form's pending state.
The proxy.ts or proxy.js file should be in the project root or src directory, at the same level as pages or app directories.
Next.js recommends Zod for server-side form validation with Server Actions. Zod is featured exclusively in the official documentation, while Yup is mentioned as an alternative but not demonstrated.
auth.config.ts contains the NextAuth configuration object (authConfig) which includes settings like pages.signIn for custom login page, and the authorized callback for request authorization checks.
Session Management tracks the user's auth state across requests. It involves creating, storing, refreshing, and deleting sessions or tokens.
Node.js runtime support for Middleware became stable in Next.js v15.5.0.
bcrypt requires a separate file because it relies on Node.js APIs not available in Next.js Proxy/Middleware (which by default runs on the Edge Runtime).
A DAL is an internal library that controls how and when data is fetched, and what gets passed to your render context. It should only run on the server, perform authorization checks, and return safe, minimal Data Transfer Objects (DTOs).
Route Handlers should be treated with the same security considerations as public-facing API endpoints. First verify an active session exists, then check if the logged-in user has the appropriate role (like 'admin') to access the Route Handler.
Authentication > Implementation Layers
37 questionscookies() is an async function that returns a promise. Usage requires await or React's use() hook.
Server Actions always execute on the server and provide a secure environment for handling authentication logic. They should treat Server Actions with the same security considerations as public-facing API endpoints and verify if the user is allowed to perform a mutation.
In Next.js 14+, closed-over variables in Server Actions are encrypted with the action ID before client transmission. A private key generated during build ensures each Server Action can only be invoked for a specific build. The .bind() method provides opt-out encryption for performance, but closed variables via this method remain unencrypted.
Import the 'server-only' package at the start of modules containing server-only code: import 'server-only'. This causes a build error if the module is imported in a client environment.
Sessions cannot be instantly invalidated (you cannot 'disconnect this customer') as there is typically no state stored about sessions on the server by default. However, in most applications, the first step upon receiving an authenticated request is to validate the user and their permissions in the database.
Next.js authentication is built on three core concepts: 1) Authentication - verifies if the user is who they say they are, requiring proof of identity such as username and password, 2) Session Management - tracks the user's authenticated state across multiple requests, and 3) Authorization - decides what routes and data the user can access.
Matcher patterns must start with '/'. They support: named parameters ('/about/:path' matches '/about/a' but not '/about/a/c'), parameter modifiers ('' for zero or more, '?' for zero or one, '+' for one or more), regex patterns in parentheses ('/about/(.)' equals '/about/:path*'), and are anchored to path start ('/about' matches '/about' and '/about/team' but not '/blog/about').
A 401 (Unauthorized) status is returned when a user is not authenticated (no session exists), while a 403 (Forbidden) status is returned when a user is authenticated but does not have the right permissions (e.g., not an admin).
Next.js recommends protecting your application at three layers: 1) Data Access Layer - adding authentication checks directly in Data Access Layer functions, 2) Route level - checking authentication in page components, and 3) UI elements - hiding sensitive components when users aren't authenticated.
No. The documentation explicitly warns: 'Middleware should not be your only line of defense in protecting your data.' Middleware is recommended for optimistic checks but should not be relied upon as the sole security mechanism.
The cookies() API is imported from 'next/headers': import { cookies } from 'next/headers'
Next.js recommends two types: 1) Optimistic Checks (via Middleware) - read only from cookies, avoid database queries, used for quick UI decisions and pre-filtering, should NOT be the sole security mechanism, and 2) Secure Checks (via Data Access Layer) - query the database to verify session validity, performed closest to data sources.
Server Actions use POST-only requests and compare the Origin header against Host/X-Forwarded-Host. Mismatch rejection prevents invocation on different hosts. Same-Site cookies provide additional browser-level protection. However, HTML sanitization is crucial since CSRF tokens aren't implemented.
Set experimental: { taint: true } in next.config.js to enable experimental_taintObjectReference and experimental_taintUniqueValue APIs. This also enables the React experimental channel for the app directory.
The middleware.ts or middleware.js file must be located in the project root or inside the src directory at the same level as pages or app. If custom pageExtensions are configured (e.g., .page.ts), name the file accordingly as middleware.page.ts.
Node.js runtime support for middleware became stable in Next.js 15.5. It was introduced experimentally in 15.2 and tested extensively on production applications before stabilization. You can enable it with: export const config = { runtime: 'nodejs' };
Next.js recommends using Jose (compatible with Edge Runtime) for JWT-based sessions, or iron-session for cookie-based stateless sessions. Jose is specifically recommended because jsonwebtoken cannot run on the Edge environment.
Next.js examples show minimum 8 characters for password length and minimum 2 characters for name length. The pattern includes at least one letter, one number, and one special character for passwords.
The Next.js documentation examples use '7d' (7 days) as the recommended default session expiration time.
No. The documentation warns: 'Do not rely on the taint API as your only mechanism to prevent exposing sensitive data to the client.' Tainting doesn't block every possible derived value (e.g., uppercasing a tainted string creates an untainted value). It should be an additional layer of protection on top of the Data Access Layer.
DTOs return only necessary, safe fields from the Data Access Layer to prevent exposing sensitive data. They should implement authorization-aware logic with conditional field inclusion based on permission checks (e.g., only exposing phone numbers to admins or team members). DTOs ensure all data requests are secure and consistent.
React Context is not supported in Server Components, restricting data flow. Child Server Components render before context providers and lack session access. You should use the taintUniqueValue API to prevent sensitive session data from being exposed to the client when sharing data.
Next.js documentation recommends using 10 rounds when hashing passwords with bcrypt: await bcrypt.hash(password, 10)
The cookies() API can read cookies in Server Components (read-only), Server Actions (read and write), and Route Handlers (read and write). HTTP does not allow setting cookies after streaming starts, so you must use .set() in a Server Action or Route Handler.
The Edge Runtime provides access to crypto, CryptoKey, and SubtleCrypto for cryptographic operations, along with comprehensive network APIs (fetch, Request, Response, Headers), encoding APIs (atob, btoa, TextEncoder, TextDecoder), and Stream APIs (ReadableStream, WritableStream, TransformStream).
Client navigations use ?_rsc=... as a cache breaker parameter during prefetch operations to prevent accidental caching of React Server Component (RSC) payloads. Middleware runs on both user clicks and prefetches with this parameter. Attackers can potentially exploit this by sending requests with the RSC header without using a cache-buster.
Secret keys should be stored in environment variables, but only the Data Access Layer should access process.env. This containment prevents secrets from spreading throughout the application.
The Data Access Layer (DAL) is a centralized internal library that controls how and when data is fetched. It should include a verifySession() function that checks session validity, use React's cache() API to memoize results during a render pass, and return only verified user information needed for subsequent requests. The DAL ensures authorization checks happen wherever data-fetching functions are called.
CVE-2025-29927 is a critical (9.1 CVSS) Next.js middleware authorization bypass vulnerability that allows attackers to bypass authorization checks by including the x-middleware-subrequest header in HTTP requests. It affects Next.js versions 11.1.4–13.5.6 and versions prior to 14.2.25 (for 14.x) and 15.2.3 (for 15.x). Self-hosted applications using middleware are vulnerable, though Vercel-hosted deployments are automatically protected.
Supported options include: name (String), value (String), expires (Date), maxAge (Number - seconds), domain (String), path (String, default: '/'), secure (Boolean - HTTPS-only), httpOnly (Boolean - prevent client access), sameSite (Boolean | 'lax' | 'strict' | 'none'), priority (String - 'low', 'medium', or 'high'), and partitioned (Boolean - CHIPS support).
Session cookie payloads should contain 'minimum, unique user data' like user ID and role. They should NOT include passwords or personally identifiable information (PII).
- experimental_taintUniqueValue - marks specific values like tokens, passwords, or keys to prevent passage to Client Components, and 2) experimental_taintObjectReference - marks entire objects to prevent passage to Client Components. Both are imported from 'react'.
Next.js recommends setting cookies with: httpOnly: true (prevents client-side JavaScript access), secure: true (HTTPS-only transmission), sameSite: 'lax' (controls cross-site behavior), path: '/' (scope within domain), and an expires or maxAge value for expiration.
Middleware uses the Edge Runtime by default, which is based on Web APIs and does not support native Node.js APIs. This means you cannot use Node.js modules like 'crypto', packages like 'jsonwebtoken', or perform filesystem operations. For JWT operations on Edge, you must use edge-compatible libraries like 'jose' instead of 'jsonwebtoken'.
The Edge Runtime explicitly disables: eval() and dynamic function generation via new Function(), WebAssembly.compile and WebAssembly.instantiate, and direct require() calls (ES Modules required). Dynamic code evaluation statements cause runtime errors unless allowlisted.
Layouts don't re-render on navigation, so auth checks shouldn't rely solely on layout logic. Additionally, returning null in layouts if unauthorized will not prevent nested route segments and Server Actions from being accessed. Place checks close to your data source or the component that will be conditionally rendered.
In production mode, React sends error hashes to clients instead of full error messages and stack traces. The hash correlates multiple errors to server logs. Development mode does not optimize for security, so always deploy production builds for production workloads.
Route Handlers > Request Processing
37 questionsThe geo object included: geo.country (country code), geo.region (region code), geo.city, geo.latitude, and geo.longitude.
Use export const dynamic = 'force-static' in your Route Handler file.
No, there cannot be a route.js file at the same route segment level as page.js.
Use NextResponse.json(body, { status: code }). For example: NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }).
No, writing cookies only works in Server Actions or Route Handlers. Reading cookies works in Server Components.
cookies() became async in v15.0.0-RC, with a codemod available for upgrading.
The second parameter is an optional context object containing params, which is a promise that resolves to an object containing the dynamic route parameters for the current route.
Yes, for backwards compatibility you can still access params synchronously in Next.js 15, but this behavior will be deprecated in the future and warnings will be shown.
The default is 10 seconds, configurable up to 60 seconds on the Hobby plan.
In Next.js 15, params is a promise that must be awaited. In version 14 and earlier, params was synchronous.
redirect() redirects to a new URL (browser shows new URL), while rewrite() proxies to a different URL while preserving the original URL in the browser.
No, 'use cache' cannot be used directly inside a Route Handler body. You must extract it to a helper function.
Yes, the revalidate value needs to be statically analyzable. For example, revalidate = 600 is valid, but revalidate = 60 * 10 is not.
set(name, value), get(name), getAll(name?), has(name), and delete(name).
No, HEAD is not automatically generated. It must be explicitly defined if needed.
Yes, the second parameter accepts both status and headers. For example: NextResponse.json({ error: 'Not Found' }, { status: 404, headers: { 'x-powered-by': 'Next.js' } }).
Yes, Route Handlers have an isomorphic Web API to support both Edge and Node.js runtimes seamlessly, including support for streaming.
Only GET methods can opt into caching. Other supported HTTP methods (POST, PUT, PATCH, DELETE, HEAD, OPTIONS) are not cached, even if placed alongside a cached GET method.
Yes, the geo and ip properties were removed from NextRequest in Next.js 15. Vercel users should use the geolocation and ipAddress functions from @vercel/functions instead.
On the Pro plan, the default is 15 seconds, configurable up to 300 seconds (5 minutes).
Using cookies(), headers(), connection(), or any Dynamic API will prevent prerendering.
Use await request.json(). For example: const data = await request.json();
Next.js will automatically implement OPTIONS and set the appropriate Response Allow header depending on the other methods defined in the Route Handler.
false (default) for indefinite caching, 0 to ensure dynamic rendering, or a number in seconds to set the default revalidation frequency.
Use await request.formData(). For example: const formData = await request.formData(); const name = formData.get('name');
It produces a response that redirects to a URL. It accepts a URL object and returns a redirect response.
Configuration > Security & Server Configuration
36 questionsNext.js creates encrypted, non-deterministic IDs to allow the client to reference and call the Server Action. These IDs are periodically recalculated between builds for enhanced security.
No. The React team warns: 'Do not rely solely on tainting for security.' Taint does not work if you extract data fields out of an object and pass them along, and derived values (like uppercasing a tainted string) will not be tainted.
Export a config object in your API route with responseLimit: false. Example: export const config = { api: { responseLimit: false } }
Next.js compares the Origin header to the Host header. If these don't match and the origin is not in the allowedOrigins list, the request will be aborted.
Yes. Wildcard domains are supported using the asterisk (*) pattern. Example: allowedOrigins: ['my-proxy.com', '*.my-proxy.com']
The last header key will override the first. Headers are evaluated in order, with later definitions overwriting earlier ones for the same path and key combination.
Variables with NEXT_PUBLIC_ prefix are exposed to the client-side and inlined into the JavaScript bundle at build time. Never use this prefix for sensitive information like API keys, database credentials, or secrets.
experimental_taintObjectReference (for data objects) and experimental_taintUniqueValue (for specific values). These prevent accidental exposure of sensitive data to the client.
Four type values: 'header', 'cookie', 'host', and 'query'. These allow conditional matching based on HTTP headers, cookies, host, or query parameters.
Set poweredByHeader: false in next.config.js. This removes the header from response headers to minimize information disclosure.
sha256, sha384, and sha512. Configure via experimental.sri.algorithm in next.config.js. Example: experimental: { sri: { algorithm: 'sha256' } }
next dev --experimental-https. To use custom certificates, provide all three arguments: --experimental-https --experimental-https-key ./path/to/key --experimental-https-cert ./path/to/cert
Yes. Vercel, the official Next.js hosting solution, adds the HSTS headers automatically.
API routes, _next/static files, _next/image optimization, favicon.ico, requests with the 'next-router-prefetch' header, and requests with 'purpose: prefetch' header.
No. When using standalone output mode, it does not trace custom server files. Standalone output and custom servers cannot be used together.
Buffer.from(crypto.randomUUID()).toString('base64'). This creates a unique, cryptographically random nonce for each request.
Use the serverActions.bodySizeLimit option. It accepts either a number of bytes (e.g., 1000) or string formats supported by the bytes library (e.g., '500kb' or '3mb'). Example: serverActions: { bodySizeLimit: '2mb' }
DENY (recommended, completely denies site being loaded within frames) and SAMEORIGIN (ensures the site is only rendered in frames on itself, not on other sites). These protect against clickjacking attacks.
Do NOT use next start with output: 'standalone'. Instead use node .next/standalone/server.js. Using next start with standalone mode creates a security vulnerability (CVE-2025-29927) in Next.js versions earlier than 12.3.5, 13.5.9, 14.2.25, and 15.2.3.
No. If you are deploying on Vercel, you will not be able to bring your custom server with you. Custom servers require a different hosting environment.
Use (await headers()).get('x-nonce') or headers().get('x-nonce'). The nonce is stored in the 'x-nonce' header set by middleware.
- When
permanent: trueis set in a redirect configuration, Next.js uses the 308 status code which instructs clients and search engines to cache the redirect forever.
v13.3.0+. The 'missing' field allows applying headers only when certain conditions are NOT met.
v14.0.0+. SRI allows maintaining static generation while enforcing integrity through build-time hash generation.
Before filesystem checks, including pages and /public files. This allows headers to be applied even to static assets.
Never use wildcards (*) in production for Access-Control-Allow-Origin. Instead, explicitly list allowed origins. For dynamic scenarios, use Vercel's Routing Middleware to set different CORS policies based on the requesting origin.
4MB. This limit exists because serverless platforms typically have payload size restrictions, and large responses can impact performance when not using a CDN or dedicated media host.
true. HTTP Keep-Alive is enabled automatically by default in Node.js versions before 18. Next.js automatically implements fetch() polyfilling with undici and activates HTTP Keep-Alive functionality.
v10.2.0+. The 'has' field allows conditional header application based on header, cookie, query, or host values.
Development: Include 'unsafe-eval' in script-src and 'unsafe-inline' in style-src for debugging support. Production: Remove these unsafe directives and use proper nonce or SRI implementation.
Set experimental.taint: true in next.config.js. Example: module.exports = { experimental: { taint: true } }
Only same-origin requests are allowed by default. The allowedOrigins configuration must be used to permit Server Action invocations from other domains.
nosniff. This prevents the browser from attempting to guess the content type if the Content-Type header is not explicitly set, protecting against MIME type sniffing attacks.
Two values: 'anonymous' (permits cross-origin requests without sending credentials) and 'use-credentials' (enables cross-origin requests with credentials included). This configuration adds the crossOrigin attribute to all , or 2) Using dangerouslySetInnerHTML: <Script id="show-banner" dangerouslySetInnerHTML={{ __html: code here }} />
beforeInteractive scripts must be placed inside the root layout (app/layout.tsx)
beforeInteractive, afterInteractive, lazyOnload, and worker (experimental)
Scripts that use the worker strategy are offloaded and executed in a web worker with Partytown.
Scripts that use the afterInteractive strategy are injected into the HTML client-side and will load after some (or all) hydration occurs on the page.
Chat support plugins and social media widgets that can wait to load during idle time.
Scripts that load with the beforeInteractive strategy are injected into the initial HTML from the server, downloaded before any Next.js module, and executed in the order they are placed.
Inline scripts require an id attribute to be defined to track and optimize the script.
No, scripts with beforeInteractive strategy are preloaded and fetched before any first-party code, but their execution does not block page hydration from occurring.
Bot detectors and cookie consent managers that are needed by the entire site.
Scripts with beforeInteractive are always injected inside the head of the HTML document regardless of where it's placed in the component.
Yes, any additional attributes (like nonce, crossOrigin, referrerPolicy, etc.) are automatically forwarded to the final, optimized