react-brain
Browse decisions
React & language foundations 3App architecture 11UI 13Platform & native 9Build, test, observe, secure 6AI in React apps 3stack composerdecision recordscensusstaleness benchnewsletterarchitecturechangelogmethodologyroadmap

entriestooling-ops · verified 2026-10-08 · react + react-native

Build tooling, bundlers & monorepo

reviewedconfidence: hightrack: heldthis tier holds 83% on the public scorecard (6/11 graded · 0 overturned) →

related decisions: security · dx · ai-devtools · p2p · react-core · meta-frameworks

cited by: react-core · typescript · p2p · dx · security

re-verified 8× — 2026-10-08 · 2026-09-24 · 2026-09-21 · 2026-09-10 · 2026-09-01 · 2026-08-18 +2 · changelog

recommendation

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

optiontradeoffevidence
MetroThe 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.111Web 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 workspacesMonorepo 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 RNManaged 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-swcRust/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.0Not 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+.232.2M/wk · ships in 10/34

evidence: npm weekly downloads (signals snapshot) · “ships in n/D” = adoption across the production-app census, honest denominators

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.

How Turbopack chunks your JavaScriptSam Poder (Turbopack team, Vercel)

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.

How Teamworks Cut Mobile Release Time to 10 MinutesCallstack (case study)

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.

Working With the React Native Super App Showcase RepositoryCallstack

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.)

Things I Learned While Building ExpoEvan Bacon (Expo core, 9 years; Expo Router creator — farewell essay, 2026-05)

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.

Rust Is Eating JavaScriptLee Robinson

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.

Vite 8.0 is out!The Vite / VoidZero team

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.

Exploring Inlined Requires: how they really workAndrei Calazans

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.

The Complete Guide to React Native Build OptimizationThe Mythical Engineer

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.

How React Native Builds Actually Work (APK, AAB, IPA, APP)Code with Beto

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.)

Sources

The full explanation

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.


See also: RB-E-NATIVE (codegen / Hermes / precompiled iOS), RB-E-DX (CI, lint, monorepo task running), RB-E-RN-VERSIONS (Hermes-V1 / Node-floor facts), RB-E-REACT-CORE (the Compiler's build plugin).

Related in tooling-ops: testing · dx · observability · security · ota