Architecture Overview
Server Runtime
Rendering Pipeline
Client Navigation
Caching and Export
Development Tools
Build and Configuration
Ecosystem Packages
Testing Infrastructure
How It Works
The following files were used as context for generating this wiki page:
Server Actions enable asynchronous functions defined on the server to be securely invoked from client components and form submissions, bridging client-side interactions and server-side mutations without requiring manual API route boilerplate. By handling execution pipelines, cryptographic bound argument verification, request routing, and router state reconciliation, Server Actions integrate seamlessly into Next.js rendering and caching systems.
Sources: packages/next/src/server/app-render/action-handler.ts:1300-1354, packages/next/src/server/app-render/encryption.ts:48-96, packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:104-147
Client-side server action invocation starts through the callServer function, which wraps action identifiers and arguments into a React transition. When invoked, callServer delegates execution to the app router's action queue via dispatchAppRouterAction.
The action queue manages asynchronous transitions, action ordering, and concurrency state through AppRouterActionQueue and dispatchAction. When an action payload is dispatched, a deferred promise is created and registered with React via startTransition. The call-chain execution walkthrough for processing actions proceeds through the following steps: dispatchAction() creates a deferred promise and ActionQueueNode, checks if actionQueue.pending === null, immediately invokes runAction() if empty, executes the action via actionQueue.action(), triggers handleResult() upon resolution, and advances the queue via runRemainingActions().
Sources: packages/next/src/client/components/app-router-instance.ts:71-144, packages/next/src/client/components/app-router-instance.ts:146-216
Note
Navigations (ACTION_NAVIGATE and ACTION_RESTORE) take immediate priority over pending actions. When a navigation action arrives while the queue is non-empty, the current pending action is marked as discarded = true so its state is never applied, and the navigation starts immediately while preserving subsequent queued nodes.
Sources: packages/next/src/client/components/app-router-instance.ts:191-208
Client-side form submissions are intercepted by <Form> components across both the App and Pages routers. The submission handler validates action URLs, inspects submitter attributes, and extracts form data into search parameters or payloads.
Sources: packages/next/src/client/app-dir/form.tsx:147-224, packages/next/src/client/form.tsx:90-169
Sources: packages/next/src/client/form-shared.tsx:3-3, packages/next/src/client/form-shared.tsx:128-132
Warning
File inputs are only supported if the <Form> component's action prop is a function (Server Action). If action is a string URL, file inputs cannot be encoded as search parameters and trigger a development warning, falling back to the filename string value.
Sources: packages/next/src/client/form-shared.tsx:83-96
Server actions often rely on closure-captured variables, known as bound arguments, which are serialized and transmitted between the client and server. To prevent tampering and ensure confidentiality, Next.js secures these closure arguments using authenticated symmetric encryption. The encryption pipeline leverages the Web Crypto API (crypto.subtle) with the AES-GCM algorithm, initialized using either an environment variable (NEXT_SERVER_ACTIONS_ENCRYPTION_KEY) or a manifest-provided key.
Sources: packages/next/src/server/app-render/encryption.ts:45-70, packages/next/src/server/app-render/encryption-utils.ts:37-91
When encoding or decoding server action bound arguments, Next.js follows a strict serialization and cryptographic pipeline. The process guarantees both confidentiality via AES-GCM and integrity by prefixing the plaintext payload with the target action ID. The call-chain execution walkthrough for encrypting bound arguments proceeds as follows: encryptActionBoundArgs() retrieves workUnitAsyncStorage and client module manifests, serializes arguments via renderToReadableStream and streamToString, invokes encodeActionBoundArg(), generates 16 random initialization vector bytes via crypto.getRandomValues(), runs encrypt() with AES-GCM using actionId + arg as plaintext, and encodes the IV and ciphertext into base64 via btoa().
Sources: packages/next/src/server/app-render/encryption.ts:72-96, packages/next/src/server/app-render/encryption.ts:108-219, packages/next/src/server/app-render/encryption-utils.ts:37-50
Conversely, the decoding pipeline executes: decodeActionBoundArg() fetches the encryption key via getActionEncryptionKey(), decodes the base64 payload via atob(), extracts the first 16 bytes as initialization vector (ivValue) and remaining bytes as ciphertext (payload), decrypts via decrypt(), checks if decrypted text starts with actionId as a checksum invariant, and returns the sliced argument payload.
Sources: packages/next/src/server/app-render/encryption.ts:48-70, packages/next/src/server/app-render/encryption-utils.ts:52-65
Warning
During decryption, Next.js explicitly verifies that the decrypted plaintext starts with the expected actionId. If this prefix check fails, it immediately throws an Invalid Server Action payload: failed to decrypt. error, preventing cross-action replay attacks and payload substitution.
Sources: packages/next/src/server/app-render/encryption.ts:65-67
Encryption utilities depend on helper functions to bridge binary buffers and string representations, safely managing V8 argument limits and raw crypto keys.
Note
The encryption key loaded via getActionEncryptionKey() is cached in the module-scoped variable __next_loaded_action_key to avoid redundant cryptographic key imports across requests.
Sources: packages/next/src/server/app-render/encryption-utils.ts:4-4, packages/next/src/server/app-render/encryption-utils.ts:67-70
Server request discovery and routing handles incoming HTTP requests to determine whether they qualify as Server Actions, parses identifying headers and multipart payload metadata, resolves action modules via global manifests, and routes execution to the appropriate worker runtime or runtime environment.
Sources: packages/next/src/server/lib/server-action-request-meta.ts:6-52, packages/next/src/server/app-render/manifests-singleton.ts:181-224
Incoming requests are inspected via getServerActionRequestMetadata() to extract header flags and payload structures. This function analyzes headers across standard Node IncomingMessage, BaseNextRequest, and web-standard NextRequest interfaces, checking for the next-action header and specific content types.
Note
URL-encoded actions are not fully supported by the action handler; however, they are permitted to flow through request metadata parsing to maintain consistent HTTP behavior when standard page components receive unexpected POST submissions. Sources: packages/next/src/server/lib/server-action-request-meta.ts:26-28
Next.js manages action bindings, server module maps, and client reference manifests globally via a MANIFESTS_SINGLETON symbol attached to globalThis. The createServerModuleMap() helper constructs a proxy that queries the server actions manifest for matching worker entries depending on the runtime environment (edge or node).
Sources: packages/next/src/server/app-render/manifests-singleton.ts:22-37, packages/next/src/server/app-render/manifests-singleton.ts:181-224
When an action request targets a specific page, selectWorkerForForwarding() evaluates whether a worker exists for the current page name. If no worker matches the active page context, it falls back to selecting the first available worker capable of handling the action ID.
Sources: packages/next/src/server/lib/server-action-request-meta.ts:6-52, packages/next/src/server/app-render/manifests-singleton.ts:181-224, packages/next/src/server/app-render/manifests-singleton.ts:250-272
Warning
If getActionModIdOrError() cannot locate an actionId in the server module map, it throws an error indicating potential deployment skew or an invalid action request from a different deployment version.
Sources: packages/next/src/server/app-render/action-handler.ts:1361-1377, packages/next/src/server/app-render/action-handler.ts:1379-1383
Once an incoming action request has been authenticated, its module ID verified, and its arguments decoded, the request enters the execution and render pipeline. This phase manages the runtime context, enforces argument limits, synchronizes cookies and revalidations across phases, and streams Flight response payloads back to the client.
Sources: packages/next/src/server/app-render/action-handler.ts:1300-1354, packages/next/src/server/app-render/use-flight-response.tsx:35-154
The execution sequence is coordinated by executeActionAndPrepareForRender(), which manages the transition of request stores from the action phase to the render phase.
Caution
The argument length check enforces SERVER_ACTION_ARGS_LIMIT = 1000. If an incoming payload supplies more than 1000 arguments, execution aborts immediately to prevent stack overflow faults during action.apply().
Sources: packages/next/src/server/app-render/action-handler.ts:1294-1319
During execution, the request store phase is explicitly managed to handle cookies, draft mode toggles, and cache revalidations.
The pipeline streams Flight responses via getFlightStream(), which resolves client module mappings and runtime-specific stream adapters (Node streams or web ReadableStream instances).
export function getFlightStream<T>(
flightStream: Readable | BinaryStreamOf<T>,
debugStream: Readable | ReadableStream<Uint8Array> | undefined,
debugEndTime: number | undefined,
nonce: string | undefined
): Promise<T>To inject hydration data outside standard React rendering, createInlinedDataReadableStream() wraps the Flight stream and formats chunks into inline script instructions using specialized bootstrap payloads.
Sources: packages/next/src/server/app-render/use-flight-response.tsx:14-18, packages/next/src/server/app-render/use-flight-response.tsx:227-266
Note
If a chunk cannot be decoded as a valid UTF-8 string due to embedded binary payloads, createInlinedDataReadableStream() falls back to serializing the buffer as base64 via Buffer.from() or btoa() inside an INLINE_FLIGHT_PAYLOAD_BINARY instruction.
Sources: packages/next/src/server/app-render/use-flight-response.tsx:191-206, packages/next/src/server/app-render/use-flight-response.tsx:256-266
When a server action completes and returns its response payload, the client-side router reducer handles cache invalidation and state tree reconciliation. The reducer inspects the revalidation status returned by the server and purges stale caches to maintain data consistency.
Sources: packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:311-352
The invalidation flow triggered by a server action response proceeds through a precise sequence of function calls to notify registered tasks and listeners:
serverActionReducer — Receives the action response state and checks if revalidationKind !== ActionDidNotRevalidate.invalidateEntirePrefetchCache — Increments both route and segment cache version counters (currentRouteCacheVersion++, currentSegmentCacheVersion++) and hands off to visible link pinging and listener notification.pingInvalidationListeners — Checks whether invalidationListeners is non-null, iterates over registered prefetch tasks, and evaluates isPrefetchTaskDirty().notifyInvalidationListener — Extracts the onInvalidate callback from the task, clears it to prevent duplicate execution, and safely invokes the user-space function inside a try/catch block.Sources: packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:311-352, packages/next/src/client/components/segment-cache/cache.ts:344-353, packages/next/src/client/components/segment-cache/cache.ts:404-441
Sources: packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:311-352, packages/next/src/client/components/segment-cache/cache.ts:344-353, packages/next/src/client/components/segment-cache/cache.ts:404-441
The router distinguishes between different forms of revalidation to optimize whether route structures, dynamic data, or static segments are cleared from memory.
Sources: packages/next/src/client/components/segment-cache/cache.ts:316-353, packages/next/src/client/components/segment-cache/cache.ts:404-440
Warning
Cache invalidation does not eagerly evict items from memory. Instead, version counters (currentRouteCacheVersion and currentSegmentCacheVersion) are incremented, causing entries to be treated as stale and lazily evicted only when next read.
Sources: packages/next/src/client/components/segment-cache/cache.ts:321-326
The Next.js development server integrates specialized tooling for action compilation diagnostics, error overlay propagation, hot reloader management, and Model Context Protocol (MCP) inspection. Development tooling bridges server-side compilation issues with client-side runtime feedback through dedicated hmr channels and dispatcher actions.
Sources: packages/next/src/server/dev/hot-reloader-turbopack.ts:1577-1622, packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx:223-272
When compilation or runtime errors occur during server action evaluation or page rendering, the development overlay queues and dispatches actions across the browser boundary using createQueuable wrappers. The error dispatcher standardizes state transitions such as build errors, unhandled rejections, and overlay toggles before rendering within a Shadow DOM container.
Sources: packages/next/src/next-devtools/dev-overlay.browser.tsx:130-240, packages/next/src/next-devtools/dev-overlay.browser.tsx:253-331
Note
Events dispatched before React mounts the dispatcher are buffered in a queue and replayed via replayQueuedEvents once maybeDispatch becomes active in useInsertionEffect.
Sources: packages/next/src/next-devtools/dev-overlay.browser.tsx:128-142, packages/next/src/next-devtools/dev-overlay.browser.tsx:293-307
The MCP diagnostics server exposes tools such as get_server_action_by_id to inspect compiled server references directly from the build output directory. The tool queries server-reference-manifest.json to resolve execution metadata for node and edge runtime environments.