Web: Vite 8 (Rolldown). React Native: Metro (+ Hermes V1, precompiled iOS binaries). Monorepo: pnpm workspaces + Turborepo. Expo for managed velocity.
dev server reachable beyond localhost (--host, server.host, Docker port) → Vite ≥8.0.5 / ≥7.3.2 (CVE-2026-39364 floor; and never publish a dev server to a network you do not control)
RN bundler alternative for module federation / super-apps → Re.Pack 5 (worth the complexity when independent teams need independent JS release cadences within one app — Callstack's own criterion)
want the Rolldown/Rollup plugin ecosystem on RN (experimental) → Rollipop (early alpha, prototype only, not an Expo path)
slow RN CI → RNRepo + Rock (prebuilt native binaries plus native build-artifact caching; they stack)
Babel is the Metro bottleneck → Noxcturnal on Expo SDK 58+ (one config flag, automatic Babel fallback; measure), else react-native-swc or facetpack for bare RN or projects Noxcturnal excludes (pin them; keep the Babel fallback)
RN render cost on Text/View-heavy screens → react-native-boost 2.0 (build-time JSX rewriting; measure your own screens and keep the escape hatch)
Pear app → esbuild-bundle to dist (desktop) or Metro + bare-pack for the Bare worker (mobile), not the web/Metro defaults — see RB-E-P2P
new web project → Vite 8
legacy web app on webpack → webpack 5.111 (zero-loader defaults, stable ESM output), else Rspack 2 for Rust build speed
want one CLI over the whole VoidZero stack (bundler, test runner, linter, formatter) → Vite+ (STABLE 1.0; Node 22.18+; its migrate command may still need manual follow-up on existing multi-tool setups)
need native control Expo does not give → bare RN (Expo vs bare: managed velocity traded for native control)
Options & tradeoffs
the field considered — and why each one isn’t the default here
option
tradeoff
evidence
Metro
The RN bundler and default. 0.87 ships with RN 0.87 (2026-08-11): source-map generation 2x faster at half the memory, TypeScript/ESM config (metro.config.mts) stable, YAML configs and .es6 extensions removed. metro 0.87.1 (2026-09-13, verified vs npm). See note: METRO 0.87.
7.1M/wk · ships in 10/34
Vite 8 / Rspack 2.2 / webpack 5.111
Web bundlers. Vite 8 runs on Rolldown (security floor 8.0.5 / 7.3.2); Rspack 2.1 runs the Rust React Compiler at 7–13x the Babel plugin, 2.2 adds ~3x faster CSS parsing and ~5% faster production builds; webpack 5.111 makes ESM output stable. vite 8.3.1, @rspack/core 2.2.7, webpack 5.111.1 (all verified vs npm 2026-09-24). See note: VITE DEV-SERVER FLOOR, RSPACK 2, WEBPACK.
232.2M/wk · ships in 10/34
Turborepo / Nx + pnpm/yarn workspaces
Monorepo task graph + caching. pnpm 12 (2026-08-26) is the stable Rust rewrite with pnpm 11's commands, settings and lockfile format; pnpm 12.6.0 (2026-09-22, verified vs npm). See note: PNPM 12.
Expo vs bare RN
Managed velocity (Expo) vs native control (bare RN).
Noxcturnal (Expo, experimental)
First-party Rust (oxc via napi-rs) Metro transform worker in Expo SDK 58 beta behind `experiments.noxcturnalTransformWorker`. Expo's numbers: ~3x faster per-module transforms, ~2x faster cold bundling — but it switches off for any project with Babel plugins and skips Reanimated/Worklets files. See note: NOXCTURNAL.
react-native-swc
Rust/SWC Metro transform, faster than Babel; custom Babel plugins need SWC equivalents (e.g. the worklets plugin for Reanimated). @react-native-swc/metro-plugin 0.1.5 (2026-05-31, verified vs npm): early.
react-native-boost 2.0
Not a faster transform: it REWRITES your JSX at build time to skip RN component layers (Text/View, Animated, ActivityIndicator, StyleSheet, Platform), so the win is render cost, not bundle time. 2.0 (2026-09-13) moves to a Metro config plugin; 2.0.2 (2026-09-18, verified vs npm). Parity with RN is the whole promise — measure. See note: REACT-NATIVE-BOOST.
facetpack (early)
@ecrindigital/facetpack — Rust/OXC Metro transformer (claims ~36x Babel transforms), one-line metro.config, Babel fallback for Flow packages, Doctor CLI. 0.2.0 (2026-01-17, verified vs npm), no release since: early-stage.
1k/wk
RNRepo (Software Mansion)
Prebuilt native binaries via Maven; cuts CI build time (~50% reported).
Re.Pack 5 (Rspack)
Metro alternative on Rust-based Rspack for module federation / super-apps. @callstack/repack 5.3.0 (2026-08-05, verified vs npm).
Rock (Callstack)
Native build-artifact caching (reported 35min → <4min); works with Metro or Re.Pack. rock 0.15.2 (2026-09-16, verified vs npm).
Rollipop (early alpha)
Metro replacement powered by Rolldown (the Rust bundler behind Vite 8): Rollup/Rolldown plugin ecosystem, Yarn PnP by default, dev dashboard; bare RN CLI only (no Expo). 1.0.0-alpha.29 (2026-07-25, verified vs npm): early-alpha, not production.
Vite+
VoidZero's unified toolchain — one CLI over Vite, Vitest, Rolldown, tsdown, Oxlint, Oxfmt; MIT; framework-agnostic. STABLE 1.0 (2026-09-28, verified vs the release post + npm), nearly 2M downloads/week. It orchestrates existing tools (not a framework, package manager, or Vite replacement) — your Vite plugins and package manager keep working. See note: VITE+.
npm weekly downloads (from the corpus's last signals run): vite 232.2M · esbuild 359.2M · @react-native/metro-config 7.1M · metro 22.9M · webpack 67.2M · @ecrindigital/facetpack 1k
Verified notes
The build layer is splitting along one line: Rust tools are replacing JavaScript tools inside the same interfaces. On the web that has already landed as the default — Vite 8 runs dev and prod on one Rust bundler (Rolldown) — so Vite is the pick for new work and webpack/Rspack are for existing configs. On React Native the bundler stays Metro, and the Rust wave arrives one layer down, as transform workers (Noxcturnal, SWC, OXC) and as prebuilt native artifacts (precompiled iOS binaries, RNRepo, Rock) that attack CI minutes rather than bundling. Monorepos take pnpm workspaces + Turborepo; pnpm 12 is the same tool rewritten in Rust.
VITE DEV-SERVER FLOOR (2026-09-21, verified vs GHSA-v2wj-q39q-566r and F5 Labs' August 2026 telemetry; surfaced by React Status #491): CVE-2026-39364 (HIGH) is a `server.fs.deny` bypass in the Vite DEVELOPMENT server. Affected: vite >=7.1.0 <=7.3.1 (patched 7.3.2) and >=8.0.0 <=8.0.4 (patched 8.0.5); the current 8.3.x line is clear. Mechanism: Vite serves host files through an internal @fs route guarded by the server.fs.deny list; query parameters such as ?raw, ?import&raw or ?import&url&inline make it skip the deny-list and return .env files, certificates or private source with a 200. It co-fires with three older bypasses of the same route (CVE-2025-30208, CVE-2025-31125, CVE-2024-45811) → treat deny-list filtering as defence in depth, not the boundary. Exposure condition: Vite binds to localhost by default and is reachable only through `--host`, `server.host` or a Docker port mapping; F5's honeynet recorded a sustained August 2026 campaign against exposed Vite dev servers (807 session-grouped attacks, ~32,000 events, cycling wordlists of env files, AWS keys, Azure tokens and IaC state files; the same cluster probed the Next.js middleware bypass CVE-2025-29927). Rule: patch to the floor, and do not publish a dev server to a network you do not control, including from CI containers and cloud dev boxes. RB-E-SECURITY owns the dev-tools-as-attack-surface thread.
NOXCTURNAL (2026-09-21, verified vs the Expo SDK 58 beta changelog of 2026-09-15): a module transformer written in Rust on oxc, bound into Node with napi-rs. Expo measured per-module transforms ~3x faster and COLD bundling ~2x faster — the largest test app, ~5,500 files, went 5.26s → 2.46s, with 443 of those files still falling back to Babel. Activation rules decide whether it helps YOUR app: it only turns on when the project has NO custom transform worker and NO Babel plugins or presets, and it SKIPS files needing the Reanimated or Worklets Babel transforms. Since Reanimated is close to universal in production RN, an existing app gets a partial win on the files that qualify, and an app with any Babel plugin gets none until Expo adds configuration (planned, per Expo). Expo says the implementation and numbers may change before stable. Warm bundling is unaffected — Metro's cache already covers it — so this is a cold-bundle and CI concern. Consequence: the Babel-bottleneck clause routes to Noxcturnal first (one flag, automatic fallback), and the third-party OXC/SWC transformers keep their slot for the projects it excludes.
FINGERPRINT `balanced` DEFAULT (same changelog): SDK 58 defaults `fingerprint` to the `balanced` preset — bumping `version` in app.json or touching files inside node_modules no longer changes the fingerprint, and native modules are hashed by package NAME AND VERSION rather than file contents. This setting decides whether CI rebuilds native or reuses a build, so a stable hash is a direct CI-minutes win; presets are configurable for a stricter tradeoff.
PNPM 12 (2026-08-26, verified vs the release post; latest re-checked 2026-09-24 vs npm: 12.6.0): the Rust rewrite is stable and deliberately not a migration — commands, flags, settings and the LOCKFILE FORMAT carry over from pnpm 11. The post told readers to `pnpm self-update latest-12` because npm's `latest` still pointed at pnpm 11; the registry has since moved, so a plain install gets pnpm 12. The real gotcha: git dependencies became IDENTITIES — for GitHub/GitLab/Bitbucket a specifier only names the repo and always resolves through HTTPS, so the lockfile never records an SSH URL; a machine that needs SSH for private repos configures `git config --global url."git@github.com:".insteadOf https://github.com/` instead of encoding it in the manifest (unknown hosts and credential-bearing URLs are kept verbatim). Wins: an unrecognized key in pnpm-workspace.yaml is now REPORTED (an error when the project pins a matching pnpm version, a warning otherwise), which closes a supply-chain hole — a misspelled `minimumReleaseAge` used to disable the install-age policy silently (RB-E-SECURITY); peer resolution cuts dependency cycles at a deterministic edge, so the lockfile is byte-identical across reinstalls and project reordering, with large cyclic workspaces resolving peers 2–3x faster on ~25% less memory; `packageImportMethod: auto` tries a hardlink before a reflink on Linux, which the post says roughly halves node_modules materialization from a warm store on btrfs; plus project-aware global bins and registry revisions. RB-E-DX owns pnpm's workspace versioning (`pnpm change` / `pnpm version -r`).
VITE+ (2026-07-02 beta, verified vs the VoidZero announcement; 2026-09-22 RC; STABLE 1.0 shipped 2026-09-28, verified vs the announcement post): one CLI over the VoidZero stack, MIT, framework-agnostic — `vp env` manages the Node runtime, `vp install` wraps your chosen package manager, and nine core commands cover scaffold-to-build. The beta was "stable, but not yet complete" with a migration command that may need manual follow-up; 1.0.0-rc.0 (2026-09-22) moved `vp test` to Vitest 5 (run `vp migrate` from the workspace root first), narrowed the CLI's Node range to ^22.18.0 || ^24.11.0 || >=26.0.0 (Node 20 dropped), removed the bundled oxlint/oxfmt executable wrappers (editor integrations move to `vp lint --lsp` / `vp fmt --lsp`), and required verified npm provenance for standalone installs/upgrades. 1.0 explicitly positions itself as NEITHER a framework, NOR a package manager, NOR a Vite replacement — it orchestrates the tools you already use (your Vite plugins, your package manager) behind one coordinated CLI and shared caching; now a real adoption target, not just new-project-only.
REACT-NATIVE-BOOST (2026-09-21 advocate pass, verified vs the v2.0.0 release): 2.0 (2026-09-13) replaces the Babel plugin with a METRO CONFIG PLUGIN — which is what makes the new optimizers possible (Animated, ActivityIndicator, StyleSheet, Platform on top of Text/View) — adds cross-file ancestor/helper analysis so it bails out less, supports Uniwind, and validates runtime parity against RN 0.88 RC. Its value proposition is behavioural PARITY with React Native while removing layers, a promise every release re-makes → measure your own screens and keep the escape hatch. No independent adoption evidence in this corpus yet.
RSPACK 2 (verified vs the Rspack 2.1 and 2.2 blogs): Rspack 2 adds RSC; 2.1 ships the Rust React Compiler (merged into SWC) through its built-in SWC loader at 7–13x the Babel plugin; 2.2 (2026-08-26) is a performance release — ~3x faster built-in CSS parsing, ~5% off production builds, and HMR that no longer refetches CSS on a JS-only change (a 4.1 MB stylesheet went ~385 ms → ~6 ms per hot update) — plus Browserslist Baseline targets.
WEBPACK CONVERGES ON THE VITE DX (5.109 released 2026-07-24, verified 2026-07-28 vs the webpack blog; 5.110 and 5.111 verified vs their posts): 5.109 flips `experiments.css` / `.html` / `.typescript` / `.asyncWebAssembly` to an `"auto"` default — each turns on UNLESS a loader is already registered for those files (built-in TS type-stripping also needs Node >= 22.6) — so existing configs behave as before and a fresh project imports CSS, HTML, TypeScript and Wasm with zero loaders; it adopts Vite's module APIs (`import.meta.glob` with eager/import/query/base/exhaustive/ caseSensitive, `import.meta.env` MODE/DEV/PROD/SSR/BASE_URL, `import.meta.resolve`) plus `?raw` / `?inline` / `?url` asset suffixes under `experiments.futureDefaults`. 5.110 adds built-in CSS/HTML minification and performance hints; 5.111 (2026-09-14) makes ESM OUTPUT STABLE, adds output.copy for static files and improves the CSS minimizer. This removes most CONFIGURATION reasons to leave webpack; the Rust-speed argument is untouched, so Vite stays the default for new web.
METRO 0.87 (verified vs the RN 0.87 blog, 2026-08-11): source-map generation is 2x faster and source maps take half the memory (both feed React Native DevTools load time); TypeScript/ESM config files (metro.config.mts) are STABLE; package self-resolve lands; support for YAML configs and .es6 extensions is REMOVED → check both before upgrading. Earlier: RN 0.79 added ~3x faster Metro cold start and removed remote-JS Chrome debugging (use RN DevTools).
ENGINE + PREBUILT NATIVE (verified vs the RN 0.84 blog, the SDK 56 changelog and the Hermes static_h blog): Hermes V1 is the DEFAULT JS engine since RN 0.84 (2026-02-11; experimental opt-in from 0.82, also opt-in in Expo SDK 55) — automatic if already on Hermes. RN 0.84 ships PRECOMPILED iOS binaries by default (fetched at pod install) and raised the minimum to Node 22.11+. Expo SDK 56 adds prebuilt XCFrameworks for its most complex modules (~16% faster median clean iOS builds); RN 0.83 artefacts and RNRepo are the same trend. Hermes also ships its own stable releases decoupled from RN (2026-07-16 check): the 2026-06 line (260318099.0.0) adds Set operations, Object/Map.groupBy, iterator helpers, Promise.withResolvers, a built-in TextDecoder and a `--transform-ts` flag (native type-only stripping à la Node --strip-types — no Babel pass). RN is on a bi-monthly release cadence.
RN BUNDLER ALTERNATIVES: Re.Pack 5 (on Rspack) is the main Metro alternative, justified by module federation (Teamworks and super-app readings). Rollipop brings Rolldown to RN directly (verified vs its repo 2026-06-30: early alpha, not production; the npm package now points at rollipop-dev/rollipop).
WEB TOOLCHAIN: a Rust wave — Vite 8 on Rolldown (1.0; ~10–30x Rollup), the Oxc family (Oxlint/Oxfmt), and Bun v1.4, which ships the Zig→Rust rewrite (2026-07, verified vs the Bun post — ~20% smaller binaries, every instrumentable memory leak resolved; bun 1.4.2 current, verified vs npm 2026-09-24; the agentic-port story lives in RB-E-AI-DEVTOOLS reading). Cloudflare acquired VoidZero (Vite/Vitest/Rolldown/Oxc) — the tools stay MIT.
CROSS-REFERENCES: RB-E-SECURITY owns the dev-server attack-surface and supply-chain threads; RB-E-DX owns lint tooling and pnpm workspace versioning; RB-E-P2P owns Pear/Bare bundling; RB-E-REACT-CORE owns the React Compiler default; RB-E-META-FRAMEWORKS owns Next.js/Turbopack.
HISTORY: 2026-10-08 CORRECTED: Vite+ 1.0 shipped 2026-09-28, not 2026-09-30 (the VoidZero post is dated Sep 28, npm 1.0.0 published 2026-09-28), and the post says "about to reach two million weekly downloads", not ~2M. 2026-10-01 GAP CLOSED: Vite+ reached STABLE 1.0 (~2M downloads/week) — the RC tracking note graduates to an adoption target, not just new-project-only. 2026-09-24 REOPENED: Vite+ beta → 1.0.0-rc.0 (Vitest 5, Node 20 dropped); pnpm latest 12.3.4 → 12.6.0; version receipts re-checked vs npm. 2026-09-21 REOPENED: Vite CVE-2026-39364 floor 8.0.5 / 7.3.2; Noxcturnal with its activation limits; fingerprint `balanced` default; webpack 5.111 stable ESM output. 2026-09-21 GAP CLOSED: react-native-boost 2.0 added on the advocate pass (build-time JSX rewriting). 2026-09-10 REOPENED: pnpm 12 `latest` on npm (12.3.4) — the post's self-update instruction dated. 2026-07-06 WEAKENED: the Rust React Compiler napi pullback is Rolldown-specific — the compiler merged into SWC and ships in Rspack 2.1. 2026-06-25 CHALLENGED → SURVIVES (high): Vite 8 shipped stable (2026-03) with Rolldown GA as the default; the Rust React-Compiler napi pullback affects only that compiler's JS path, not Rolldown-as-bundler; Metro stays the RN default.
Canonical reading
Editorial annotations on why each piece matters — the articles themselves are the originals; read them there.
The chunking-strategy explainer plus the Next.js 16.3 knobs that come out of it — the first thing to read when a Next app's problem is 'too many requests' or 'too much JS per page'. The framing is a three-way tradeoff with no free answer: one chunk for everything caches perfectly but over-ships; one chunk per page never over-ships but duplicates shared code into every page; one chunk per module never duplicates but explodes the request count. Turbopack's default sits in between by MERGING chunks at build time, weighting a single-page visit at 2/3. Measured on nextjs.org itself, that default cut requests by more than half versus no merging while shipping slightly less code, whereas maximum merging cut requests further but shipped ~10% more overall — better for a visitor who lands once, worse for one who navigates. The 16.3 features address the two structural limits (merging happens before anyone visits, and the algorithm has to guess how people browse): experimental.turbopackChunking.generateComponentChunks emits un-merged chunks alongside merged ones so the RUNTIME picks whichever is cheaper given what the browser already has; firstPageLoadPriority (default 0.67, bounce rate is a sensible starting value), priorityRoutes and clusters let you replace the guesses with your own analytics; and three ship-less-code flags — experimental.turbopackCjsTreeShaking (CJS tree-shaking was previously ESM-only), experimental.turbopackSharedRuntime (one runtime chunk instead of per-page: one fewer blocking request and ~10 KB per navigation), and a default runtime that no longer ships WebAssembly/Web Worker code unless detected. All experimental in 16.3; the CJS and shared-runtime flags are stated as future defaults.
The production receipt for the Re.Pack/Module-Federation lane: Teamworks' seven mini-apps moved off Metro onto Re.Pack + Module Federation with a shell app and two CI/CD lanes (host vs mini-app), cutting change lead time from 1-2 days to ~10 minutes with multiple staging deploys per day. Vendor case study (Callstack builds Re.Pack) — but the numbers are client receipts, and it grounds this entry's own when-clause: module federation is worth the complexity when independent teams need independent release cadences in one app.
The HOW behind the Teamworks receipt above: Callstack's open-source Super App Showcase (Re.Pack + Module Federation V2, monorepo) explained role by role — a Host Shell that is the ONLY package with native binaries (owns root nav, boot, auth gates, remote origins), federated mini-apps each exposing one navigator/context as a remote bundle, and a singleton Shared SDK for anything that must exist exactly once (streaming connections, shared state, design tokens). Two non-negotiable rules carry the architecture: native code lives only in the host shell (a doubly-registered native module crashes outright, no soft warning), and every shared dep reads version AND requiredVersion from one dependency catalog because Module Federation does not enforce strict equality itself. (Fetched via browser-UA curl; callstack.com 403s WebFetch.)
The philosophical background behind this entry's Expo-vs-bare row, from the person who built much of it: dogfooding as the design tool, users articulating symptoms not solutions ('a runtime that was theirs, not ours' → dynamic runtime generation / prebuild), trust compounding through the expo-eject criticism years, and telemetry settling architecture debates. Durable platform-building judgment, found by the 2026-07-16 back-audit of TWiR #284.
The first-principles explainer for WHY the JS toolchain is going Rust — memory safety, no GC, real parallelism, the redundant-parsing problem — mapping the whole landscape (esbuild, SWC, Oxc/Biome, Rspack, Rolldown). Durable cross-cutting context, not a single release.
Why maintaining two pipelines (esbuild + Rollup) was unsustainable and how Rolldown unifies dev/prod on one Rust bundler (10-30x faster), with Oxc semantic analysis driving better tree-shaking. The primary source for the Vite+Rolldown+Oxc consolidation.
Exactly what Metro's inlineRequires Babel transform does, why React/RN stay hoisted, why default imports aren't inlined, and how rnx-kit's esbuild path differs (real tree-shaking vs startup-only). The canonical Metro startup-mechanics reference.
Why RN Android builds are slow (conservative Gradle/Metro/C++ defaults) and how parallel Gradle, dynamic Metro workers, ccache, and single-architecture builds cut times from 20+ min to 2-5 min, with benchmarks. The RN-CI build-perf dimension the other readings here don't cover.
Watching
Curated talks, podcasts & videos — the A/V companion to the reading list.
Walkthrough of what actually happens between `npx expo run` and an installable artifact — the Gradle/Xcode stages behind APK/AAB/IPA and where Metro's bundle lands in them. The A/V companion to this entry's build-pipeline facts. (Verified via YouTube oEmbed: title + author.)
The reviewed long-form essay behind this entry — the why, not a how-to. Also on GitHub.
About build tooling, bundlers & monorepos (React & React Native)
Diataxis: Explanation. This page builds understanding of the build/bundler decision —
the reasoning behind the pick. It is not a tutorial: the candidate list and one-line
tradeoffs live in the index entry RB-E-BUILD; the feedback-loop side (CI, lint, hooks) is
RB-E-DX. Read this for the why.
Two toolchains, one trend
The first thing to get straight: web and React Native have separate build toolchains, and
they're moving at different speeds.
Web is in the middle of a churn — the "bundler wars" — that is now resolving toward
Rust. Vite 8 ships on Rolldown (a Rust bundler), unifying what used to be two
pipelines (esbuild for dev, Rollup for prod). The broader wave is the same: SWC, the Oxc
family (Oxlint/Oxfmt), Bun, Rspack — all Rust/native, all chasing speed. Cloudflare
acquiring VoidZero (Vite/Vitest/Rolldown/Oxc) signals this consolidation; the tools stay MIT.
React Native is steadier: Metro remains the default bundler, and the action is in
the native build (Gradle/Xcode) and the JS engine, not in swapping bundlers.
The durable trend across both is Rust is eating the toolchain — not as fashion, but
because native code without a GC buys real, compounding build-speed wins. That's the lens for
reading any specific tool choice.
The default, and why
Web: Vite 8 (Rolldown). React Native: Metro (+ Hermes V1, precompiled iOS binaries).
Monorepo: pnpm workspaces + Turborepo. Expo for managed velocity.
On the web, Vite is the default for new projects — fast dev server, and now a unified Rust
production bundler. On React Native, Metro stays the default not by inertia but because
it's tightly integrated with RN's resolution, Hermes, and the New Architecture's codegen;
RN 0.79 already gave it ~3× faster cold start. The engine question is settled too: Hermes
V1 is the default JS engine since RN 0.84, which also shipped precompiled iOS binaries
(faster pod install builds) and raised the floor to Node 22.11+ (verified against the
RN 0.84 blog). For monorepos, pnpm workspaces + Turborepo give a cached, parallel task
graph. And Expo is the managed-velocity choice — it owns a large slice of the build/release
pipeline so you don't have to.
The landscape, and when each one wins
Metro — the RN default; integrated, Hermes-aware, New-Arch-codegen-aware. You don't replace
it casually. RN-side build speed problems are better solved by caching and transforms than by
swapping the bundler (below).
Vite 8 / Rspack 2 / webpack (web) — Vite (on Rolldown) is the new-project default; Rspack
(Rust) is the webpack-compatible high-performance path and adds RSC support; webpack remains for
legacy. The selection axis is greenfield-vs-legacy and how much you value Vite's DX. webpack
5.109–5.111 narrowed the DX gap (zero-loader CSS/HTML/TS/Wasm, Vite's import.meta APIs, stable
ESM output), so a working webpack build has fewer configuration reasons to move; the Rust-speed
argument for Vite and Rspack is unchanged.
The Vite dev-server floor — pin Vite to at least 8.0.5 / 7.3.2. CVE-2026-39364 lets an
exposed dev server hand out .env files and keys, and exposed dev servers were mass-scanned in
August 2026. It only bites when the server is reachable beyond localhost (--host,
server.host, a Docker port mapping) — so don't publish a dev server to a network you don't
control (RB-E-SECURITY owns the thread).
Re.Pack 5 (Rspack) — the main Metro alternative for RN, on Rust-based Rspack; reach for it
specifically for module federation / super-apps, not as a general Metro replacement.
Noxcturnal (Expo SDK 58+, experimental) — Expo's first-party Rust (oxc) Metro transform
worker, one config flag with automatic Babel fallback; Expo measured ~2× faster cold bundling.
It is now the first thing to try when Babel is the Metro bottleneck on Expo — but it only
activates in projects with no Babel plugins or presets and skips Reanimated/Worklets files, so
most existing apps get a partial win.
react-native-swc / facetpack — third-party Rust Metro transforms, faster than Babel; the
answer for bare RN or for projects Noxcturnal excludes (note: custom Babel plugins need SWC
equivalents — e.g. the worklets plugin for Reanimated). Both are early; pin them.
react-native-boost — a different lever: it rewrites JSX at build time to skip RN component
layers, so it cuts render cost, not bundle time. Its promise is behavioural parity with RN, so
measure your own screens.
RNRepo / Rock (native build caching) — RN's slowest step is usually the native build, not
the JS bundle. RNRepo (prebuilt native binaries via Maven) and Rock (native build-
artifact caching) attack that directly — the lever for slow RN CI, with large reported wins.
On Expo, SDK 58's balanced fingerprint default keeps the native hash stable across version
bumps and node_modules noise, so CI reuses native builds more often.
Turborepo / Nx + pnpm/yarn workspaces — monorepo task graph + caching; the standard way to
make many-package repos build incrementally. pnpm 12 (2026-08-26) is the Rust rewrite; it keeps
pnpm 11's commands, settings and lockfile format, but git dependencies now always resolve over
HTTPS (configure SSH with a git insteadOf rule, not in the manifest).
Expo vs bare RN — managed velocity vs native control. Expo owns more of the toolchain
(builds, updates, config); bare gives full control at the cost of owning it yourself.
Tradeoffs and failure modes to name out loud
Swapping Metro to "go fast." RN's bottleneck is usually the native build or Babel, not
Metro itself. Reach for native build caching (Rock/RNRepo) or a Rust transform (Noxcturnal on
Expo, else react-native-swc) before
replacing the bundler; replace Metro (Re.Pack) for module federation, not for raw speed.
Chasing the newest Rust tool mid-project. The web toolchain is consolidating fast;
rewriting a working build to the bundler-of-the-month is churn. New project → Vite; existing
working build → migrate only on a concrete pain.
Letting Babel rot the RN build. If the Metro transform is the bottleneck, that's a
fixable, measurable problem (Noxcturnal on Expo SDK 58+, else react-native-swc) — but mind the
custom-plugin parity gap, which is also what keeps Noxcturnal off for many apps.
Confusing build with DX. The bundler is one piece; the feedback loop (CI gates, lint,
hooks) is what keeps the build honest — that's RB-E-DX, not this entry.
How it interacts with the rest of the stack
Native (RB-E-NATIVE). Codegen, precompiled iOS binaries, and Hermes V1 are the build-
time face of the New Architecture; this entry and that one describe the same machine from the
build and the runtime side.
DX (RB-E-DX). Monorepo task running, CI, and lint/format live there; the bundler choice
feeds into that loop.
RN versions (RB-E-RN-VERSIONS). Hermes-V1-default, precompiled iOS, and the Node-22.11
floor are version-pinned facts; verify a specific row against the RN blog.
React core (RB-E-REACT-CORE). The React Compiler plugs into the build (Babel/SWC/Oxc/
Vite); your bundler choice determines how you enable it.
In one paragraph
Treat web and RN as two toolchains with one shared trend — Rust is eating the build for
speed. On the web, default to Vite 8 (Rolldown) and only migrate a working build for a
real reason. On React Native, keep Metro (with Hermes V1 default and precompiled
iOS since RN 0.84), and fix slowness where it actually is — native build caching (Rock/RNRepo)
or a Rust transform (Noxcturnal, SWC) — rather than swapping the bundler (Re.Pack is for module federation, not
raw speed). Use pnpm + Turborepo for monorepos and Expo for managed velocity, and keep
the feedback-loop concerns in RB-E-DX.