Server Bundles
Give expensive routes their own Rust executable and deployed function using bundle.toml
Server bundles keep expensive dependencies and runtime files out of ordinary functions. Each bundle is compiled independently and deployed as one function. Public URLs and generated browser clients stay the same.
This feature currently requires the framework and CLI from this repository's source; it is not part of the published 0.6.1 framework / 0.3.0 CLI. Until the next coordinated release, run the checkout's CLI, for example:
cargo run -p cargo-nextrs --bin nextrs -- bundles plan --root examples/react-todos
The shorter nextrs commands below assume that source-built CLI is on your PATH.
Assign a route or subtree
Put a bundle.toml beside your route conventions:
app/
page.tsx # default bundle
api/
documents/
bundle.toml # documents bundle
route.rs
[id]/route.rs # inherits documents
status/
bundle.toml # can override the parent
route.rs
# app/api/documents/bundle.toml
bundle = "documents"
Assignments inherit down the directory tree. The nearest named assignment wins;
without an assignment, an endpoint belongs to default. All HTTP methods in a
route.rs stay together. A colocated page also belongs to the same bundle.
Directory names and public URLs do not change.
To return a nested route to the default function:
# app/api/documents/status/bundle.toml
bundle = "default"
Isolate one endpoint
# app/api/video/render/bundle.toml
isolate = true
features = ["video"]
assets = ["resources/video"]
This creates a stable, private bundle name for this endpoint. It includes its
methods and required ancestor conventions. Unlike a named assignment,
isolate = true applies only to the colocated endpoint; descendants continue to
inherit the nearest named ancestor. Use a named bundle to group a subtree.
Names use lowercase letters, digits, and hyphens. The route- prefix is reserved
for generated isolated names. A declaration without an endpoint is an error.
Keep dependencies separate
A routing tag alone cannot remove an unconditional Cargo dependency or shared initialization code. Make heavy dependencies optional:
# Cargo.toml
[features]
default = ["documents"] # convenient for ordinary local development
documents = ["dep:pdf-engine"]
[dependencies]
pdf-engine = { version = "1", optional = true } # replace with your actual engine
Declare features and private runtime files for named bundles centrally:
# nextrs.toml (alongside the existing [app] and [vercel] tables)
[deployment]
features = [] # Cargo features common to every bundle
[bundles.documents]
features = ["documents"]
assets = ["resources/fonts", "resources/templates/invoice.html"]
Assets are explicit files or directories relative to the application root; globs
and symlinks are not supported. They retain their relative paths inside the
function. For example, the handler opens resources/templates/invoice.html.
Keep private runtime resources outside public/, which is served by the CDN.
Each bundle gets a separate Cargo invocation with --no-default-features and
only its declared features. The framework excludes other endpoints' Rust modules
before compiling. Gate heavy modules in src/ and their initialization with
#[cfg(feature = "documents")] too: shared application code is compiled in every
bundle. Ordinary Cargo builds do not select a bundle and still contain all routes.
Inspect and build
nextrs bundles plan
nextrs bundles build # native release binaries
nextrs bundles build --dev # native debug binaries for testing
nextrs bundles build --bin my-server # choose the process adapter
nextrs bundles build --vercel # Linux executable functions + routing
Every command accepts --root path/to/app. Native builds use the package's
default-run binary, falling back to the package name. Vercel builds use index,
require cargo-zigbuild and Zig, and target x86-64 Linux.
Native output lives in .nextrs/bundles/<name>/executable. Run each process from
its bundle directory so relative runtime asset paths resolve. Configure
NEXTRS_PUBLIC_DIR if serving public assets from a separate directory. Use the
manifest's route ownership to configure your self-hosted reverse proxy.
The builder checks a receipt from the application build script against the route plan and refuses to package an older framework that ignored bundle selection.
Vercel output lives in .vercel/output/functions/__nextrs_functions/<name>.func.
The output also contains routing.json (the generated dispatch rules),
bundle-manifest.json (ownership/features/assets) and
bundle-artifacts.json (executable sizes and declared inputs). These files are
build reports, not public assets. Frontend client generation must run before a
standalone bundle build if the app uses a generated client.
Deploy and route requests
nextrs deploy --preview
nextrs deploy
For apps with multiple bundles, nextrs deploy prepares the complete browser
client once, builds each server executable, copies public assets, and uploads
one prebuilt deployment. It preserves configured Vercel regions and cron paths.
Custom [vercel] build/install commands and raw extra settings are currently
rejected for split deployment rather than silently dropped. Advanced deployments
can explicitly adapt output from nextrs bundles build --vercel.
The platform routes directly to the owning function. All methods for a URL go to that owner, allowing Axum to preserve HEAD and method-not-allowed behavior. Static routes precede dynamic and catch-all routes. Unknown paths reach the default function for 404 rendering. Internal function URLs are blocked from public dispatch. Cookies, authorization, query strings, request bodies, response headers, and streaming continue through the normal executable runtime adapter.
The generated Vercel project configuration deliberately rejects stock cloud builds for split apps: use the prebuilt commands so a monolithic executable cannot accidentally be deployed in place of the selected bundles.
Shared behavior and current limits
Ancestor middleware, layouts, loading states, and not-found conventions required by a selected route are included with it. Shared state is process-local: use an external database or service when bundles must share data.
Prefetch-backed React pages must remain in default for this first version.
Moving one into a separate bundle produces a build error. Direct Rust calls
across a bundle boundary are not converted into RPC; cross-bundle prefetch and
remote server calls are future work. Full OpenAPI/client generation runs against
the unpartitioned application before building server bundles.
The React Todos example
demonstrates /api/exports in an exports bundle with an optional CSV dependency
and a private resource directory. The default function omits both.