Build Pipeline
Vinext’s build pipeline transforms your Next.js application into production-ready bundles for deployment. This guide covers the build process, optimization strategies, and deployment preparation.Build Orchestration
Using createBuilder
Callingbuild() from the Vite JS API doesn’t trigger the RSC plugin’s multi-environment build pipeline.
From the CLI (packages/vinext/src/cli.ts):
Build Sequence
ThebuildApp() method runs a 5-step build pipeline:
-
RSC environment build
- Bundles server components
- Applies
react-serverimport condition - Generates RSC runtime modules
-
SSR environment build
- Bundles SSR runtime
- Applies
nodeimport condition - Links to RSC chunks
-
Client environment build
- Bundles browser code
- Code-splits by route and shared dependencies
- Applies
browserimport condition
-
Manifest generation
- Maps source modules to output chunks
- Used for preload hints and modulepreload
-
Asset optimization
- CSS extraction and minification
- Image asset copying
- Compression (gzip/brotli)
Code Splitting Strategy
Manual Chunks
Vinext uses a conservative code-splitting strategy optimized for real-world performance: Frompackages/vinext/src/index.ts:
Output Configuration
experimentalMinChunkSize merges tiny shared chunks (< 10KB) back into their importers:
- Reduces HTTP request count
- Improves gzip compression efficiency (small files restart the compression dictionary)
- Adds ~5-15% wire overhead for many small files vs fewer larger chunks
Why Not Per-Package Splitting?
Many bundlers split every npm package into its own chunk. Vinext deliberately doesn’t: Problems with per-package splitting:- Creates 50-200+ chunks for typical apps (exceeds HTTP/2 sweet spot of ~25 requests)
- gzip/brotli compress small files poorly (each file restarts with empty dictionary)
- ES module evaluation has per-module overhead that compounds on mobile
- No major Vite framework (Remix, SvelteKit, Astro, TanStack) uses per-package splitting
- Next.js only isolates packages > 160KB
- Shared dependencies between routes get their own chunks automatically
- Route-specific code stays in route chunks
- Results in 5-15 vendor chunks based on actual usage patterns
Treeshaking
Aggressive Vendor Treeshaking
Vinext uses aggressive treeshaking to eliminate unused exports from vendor packages:moduleSideEffects: "no-external" means:
- Local project modules: preserve side effects (CSS imports, polyfills)
- node_modules packages: treat as side-effect-free unless exports are used
"no-external", only the used renderer is included (~50KB).
Example: @mui/material
Importing Button from the barrel doesn’t pull in the entire library (80+ components). Only Button and its dependencies are included.
Why Not the “smallest” Preset?
Vite’s"smallest" preset also sets:
propertyReadSideEffects: false- Can break libraries that rely on property access side effectstryCatchDeoptimization: false- Can break feature detection patterns
"recommended" + "no-external" gives most of the benefit with less risk.
Lazy Chunk Detection
Vinext computes which chunks are lazy-loaded (behindReact.lazy(), next/dynamic, or manual import()) and excludes them from preload hints:
__VINEXT_LAZY_CHUNKS__ and excluded from <link rel="modulepreload"> and <script type="module"> tags. They’re fetched on demand when the dynamic import executes.
Static Export
Theoutput: 'export' option renders all pages to static HTML at build time:
From packages/vinext/src/build/static-export.ts:
Static Export Constraints
Pages Router:- ✅ Static pages
- ✅
getStaticPropspages - ✅ Dynamic routes with
getStaticPaths(must befallback: false) - ❌
getServerSideProps(build error) - ❌ API routes (skipped with warning)
- ✅ Static pages
- ✅ Dynamic routes with
generateStaticParams() - ❌ Dynamic routes without
generateStaticParams()(build error) - ❌ Route handlers (skipped with warning)
Cloudflare Workers Build
For Cloudflare Workers deployment, Vinext applies additional transformations:Embedded Manifests
The SSR manifest and lazy chunk list are embedded as globals:manifest.json at runtime (Cloudflare Workers has no file system).
Native Module Stubbing
Native Node.js modules (sharp, resvg, satori) are auto-stubbed for Workers:Production Optimizations
Compression
Vinext applies compression to static assets:Cache Headers
Vinext sets cache headers based on content type: Hashed assets (JS/CSS with content hash in filename):Asset Collection
The Pages Router SSR entry collects assets for each page:Next Steps
Architecture Deep Dive
Core architecture and design decisions
RSC Integration
React Server Components integration
Virtual Modules
Virtual module system explained
Deployment
Deploy to Cloudflare Workers