Open source · MIT · on npm

Run dozens of Expo dev servers on one machine

jetplane is a Metro plugin and a lightweight dev server for Expo & React Native. Every same-dep project shares one transform cache, so each environment costs about 40 MB — not Metro’s ~325 MB idle / ~2 GB cold.

$npm install jetplane
$npx jetplane dev

Just want the cross-project cache? Run jetplane init and keep using expo start.

Benchmark · warm path

Memory and warm starts, measured honestly

These are warm-path numbers. jetplane isn’t racing Metro on a cold build — it moves the heavy build to pre-warm time, then serves the pre-built bundle cheaply and shares one transform cache across projects. It complements Metro rather than competing with it: the steady state gets cheap, and the cold-bundle spike that scales linearly across servers goes away.

Dev-server memory (MB) · lower is better
IdlePeak (under load)
jetplane
thin serve · no Metro
40
68
Metro (Expo)
React Native · per project
325
2,018

jetplane in colour; Metro and the web dev servers muted for context. jetplane’s bar is a warm serve of a pre-built bundle — the heavy build ran once at pre-warm time — so this is a footprint comparison, not a like-for-like cold build against Metro. Linear scale — Metro’s cold bundle (~2,018 MB) runs off the axis (torn end) so the single-process servers stay comparable. Resident memory of the whole dev-server process tree (psRSS), Apple Silicon · macOS 15 · bench/RESULTS.md

99.9%
cross-project cache hit-rate
1,440 / 1,442 modules · 3 separate projects
4.3×
faster warm bundles
3,205 ms cold → 753 ms with a warm cross-project cache
~40 MB
per dev server
thin serve of a pre-built bundle · vs Metro ~325 MB idle
0.38 ms
warm packed boot
serving an already-built bundle — the build ran earlier

How it works

A dev runtime built on the vendor / app split

Validated end-to-end on a real device in Expo Go.

node_modules is ~98.5% dead weight

For a real Expo app only ~8 MB of 539 MB is reachable into the bundle. jetplane splits the immutable vendor layer from the tiny mutable app layer and caches the vendor once.

Cross-project transform cache

Transforms are content-addressed by source bytes (root-independent), so the same module transforms once and is reused across different projects — which Metro’s own root-dependent cache cannot do.

Thin, no-Metro dev server

A pre-built bundle is served from a thin process that mmaps it (shared physical pages). No per-project Metro means ~40 MB per environment instead of ~325 MB idle / ~2 GB cold.

Live HMR, reconstructed

Fast Refresh works on the thin server: the /hot WebSocket speaks Metro’s protocol and updates are rebuilt from the bundle + the hot transform — validated on device in Expo Go.

Shared transform service

babel’s ~150 MB resident cost is paid once by a shared service that many thin servers call — so per-project memory stays flat and babel is amortized across the whole fleet.

Built for fleets

~40–55 MB × N + one shared service, versus Metro’s ~325 MB × N idle and ~2 GB × N cold-bundle spikes. A burst of cold bundles is what OOMs a fleet node — jetplane removes it.

Compatibility

Is it a drop-in replacement?

Not wholesale — and it doesn’t need to be. The cache plugin drops into your existing expo start with one line; the experimental thin serve replaces the dev-server role at ~40 MB. It augments Metro; it doesn’t replace the Expo CLI.

CapabilityMetro · expo startjetplane cache pluginjetplane thin serve
Drop-in with the Expo CLI (expo start)it is Metro+1 line in metro.config.jsseparate serve command
Runs in Expo Go
Cross-project transform cacheMetro's keys are root-dependent
Cold-bundle ~2 GB spikeyesavoided after 1st buildnone
Per dev-server memory~325 MB idle · ~2 GB cold~325 MB (rides Metro)~40 MB
HMR / Fast Refreshapp-layer
Full on-demand bundling (any entry, symbolication)pre-built bundle + app-layer HMR
Replaces Metroaugments Metroserve role only
Setupnone1 linebuild step + serve