FeaturesEdit this page.md

Server function limits

@lazarv/react-server enforces resource ceilings on every inbound RSC reply (server function call) before any of your server function code runs. These limits cap the cost of payload deserialization and protect the server from denial-of-service vectors that exploit the rich RSC reply wire format — deeply nested values, oversized strings, huge BigInts, unbounded streams, and so on.

The defaults are aligned with React's upstream limits introduced in the post-CVE hardening (CVE-2025-55182 and related). For most applications you will never need to touch them. They become relevant when you either need to tighten the ceilings for high-risk public endpoints, or loosen them to allow legitimately large server function payloads (file uploads, batched mutations, large bound argument arrays).

The reply decoder runs as the very first step of handling any server function invocation. It parses the wire payload (a JSON string or a multipart/form-data FormData), enforces the limits below, and only then dispatches the call to the resolved server function.

If a request exceeds any limit, the decoder throws a DecodeLimitError that is propagated back to the client through the standard server function error path. Your server function code is never invoked. The error includes the name of the limit that was exceeded and the observed value, which makes operational triage straightforward.

Because the limits are enforced inside the decoder, they cover both:

LimitDefaultWhat it caps
maxRows10000Number of outlined rows (chunks) per reply
maxDepth128Recursion depth when materialising a row's value tree
maxBytes32 MiBTotal payload size (sum of FormData entry sizes)
maxBoundArgs256Bound arguments on a server reference (matches React)
maxBigIntDigits4096Digits in a decoded BigInt literal (matches React)
maxStringLength16 MiBLength of a single string row before decoding
maxStreamChunks10000Chunks materialised for a decoded ReadableStream, AsyncIterable, or Iterator

Each limit is independent — overriding one does not reset the others to their defaults.

Set limits under serverFunctions.limits in your react-server.config.mjs:

react-server.config.mjs
export default { serverFunctions: { limits: { // Tight ceilings suitable for a public mutation endpoint: maxBytes: 1 * 1024 * 1024, // 1 MiB maxDepth: 32, maxBoundArgs: 16, maxStreamChunks: 1000, }, }, };

Any field you omit keeps its default value, so you only need to specify the ceilings you want to override.

For a public-facing app that does not need large payloads, drop the byte ceiling and depth substantially:

react-server.config.mjs
export default { serverFunctions: { limits: { maxBytes: 256 * 1024, // 256 KiB maxStringLength: 64 * 1024, maxRows: 1000, maxDepth: 32, }, }, };

For an internal tool that legitimately uploads large files or streams:

react-server.config.mjs
export default { serverFunctions: { limits: { maxBytes: 256 * 1024 * 1024, // 256 MiB maxStreamChunks: 100_000, }, }, };

Loosening limits trades safety for capacity. Combine with rate limiting, authentication, and request-size limits at your reverse proxy layer when raising ceilings on internet-facing endpoints.

The limits work in concert with the decoder's structural defences (which are always on and not configurable):

The configurable limits in this document add a separate, resource-bounding layer on top of those structural defences. Together they ensure that an attacker cannot:

  1. Pollute prototypes or reach forbidden property paths via crafted references.
  2. Smuggle callables into your server function arguments.
  3. Force your server to perform unbounded work (CPU, memory, or stack) just by sending a malformed reply.

When a limit is exceeded the decoder throws a DecodeLimitError. Like other server function errors, it is surfaced to the client through the RSC error path. You can catch and present it via your error boundary, or inspect it inside a custom HTTP middleware to log, alert, or block the offending caller.

The error carries:

Because rejection happens before any server function runs, exceeding a limit has zero side effects beyond the rejected request itself.