Rust replaced almost the entire JavaScript build toolchain by 2026. Turbopack, Rolldown, Biome, oxlint, Bun. Why npm security made it inevitable.
Rust Owns the JavaScript Toolchain in 2026
TL;DR
If you are shipping a modern JavaScript app in 2026, almost every step between your editor and your production bundle is a Rust binary. Next.js builds with Turbopack. Vite builds with Rolldown. Your linter and formatter are Biome or oxlint. Your CSS pipeline runs through Lightning CSS. Bun, the runtime that started in Zig, just merged its Rust rewrite into main. The result: bundlers are 5 to 30x faster, linters are 50 to 100x faster, and the dependency tree of "the JavaScript toolchain" has collapsed to a handful of statically linked binaries with zero npm postinstall scripts. That last detail matters more than it sounds, because the npm supply chain spent the last 18 months getting set on fire.
Why I'm writing this
Three threads, one direction. Worth writing down.
The toolchain went Rust
Pick a step in the build pipeline and chances are it has been quietly rewritten in Rust.
next dev and next build. The numbers Vercel published before the cutover: more than half of all dev sessions and more than a fifth of production builds were already on Turbopack via 15.3+. After the default flipped, the 16.2 release in April claimed roughly 87% faster dev startup on real apps. The HMR feels different. I had stopped noticing how often I was alt-tabbing during compiles, and after switching I noticed how often I was not.rolldown-vite package was archived a week after 8.0 because it was redundant. If you npm create vite@latest today, you get Rolldown without asking for it.tsgolint (which itself runs on Microsoft's Go-based tsgo). Shopify reported a 71% lint-time reduction across an ~80,000 file TypeScript codebase. The pattern teams actually use: oxlint as a pre-commit and CI gate for the hot rules, Biome for formatting, ESLint kept around for any plugin you cannot get rid of yet.dx to replace npx, and started using tsgo for type checking. Node 25.2 (November 2025) shipped stable TypeScript support through Amaro 1.0, which is a wrapper around @swc/wasm-typescript. Node's official type-stripping path is now SWC compiled to WebAssembly, shipping inside core. There is more Rust running inside node than most teams realize.Bigger than dev tools
Win32k.sys, Hyper-V, the SymCrypt cryptographic library, and Azure Data Explorer. The Azure SDK for Rust shipped beta in February 2025 and now releases monthly alongside the other language SDKs. At RustConf 2025, Microsoft's Galen Hunt described the broader internal goal as refactoring roughly one million lines of code per month for the rest of the decade, with the aim of eliminating C and C++ from Microsoft's codebase by 2030.What about mobile
Mobile is a different shape and Rust shows up differently.
The default cross-platform stacks (React Native, Flutter) are not Rust. React Native's New Architecture (Fabric, TurboModules, JSI) is C++. Hermes is C++. Flutter is Dart plus C++. What changed in the last 18 months is the layer just above that: Rust as a shared core, exposed to those frameworks through binding generators.
The pattern teams actually use: write the things that should not be rewritten twice in Rust, and put a thin native UI on top of them.
- Signal does this with
libsignal. The protocol, AES-GCM and other primitives, zero-knowledge groups, and remote attestation are all Rust crates. Java, Swift, and TypeScript wrappers expose them to Android, iOS, and Desktop. - 1Password rewrote its core (sync, storage, crypto, permissions) in Rust, kept native UI per platform, and built TypeShare so type definitions stay consistent across the FFI boundary. One Rust core, eight clients on top.
- Mozilla ships sync, login storage, browsing history, push, and experimentation as Rust components shared between Firefox iOS and Firefox Android, all generated through UniFFI.
- Cloudflare ships BoringTun, a userspace WireGuard implementation in Rust, on millions of consumer iOS and Android devices through Mozilla VPN and others.
- Bitwarden is moving its cryptographic operations into a shared
bitwarden_coreRust SDK consumed by every client.
flutter_rust_bridge doing the same job for a while, and v2 is now an officially Flutter Favorite package with async Rust support, web targets, and zero-copy big arrays.The honest tradeoffs on mobile have not gone away.
- Toolchain pain. NDK plus
cargo-ndkfor Android. Xcode, codesigning, provisioning, and a sometimes fragilelipo/XCFramework dance for iOS. Every Rust target multiplies your CI matrix. - String impedance. Rust is UTF-8, Swift is UTF-16, Java/Kotlin is "modified UTF-8." Conversions are easy to get wrong, and "wrong" means corruption or crashes.
- FFI overhead is real. The well-worn guidance: batch your calls. One call processing 100 items beats 100 calls processing one.
- Binary size. A naive Rust core can add MB to your IPA or APK before optimization. LTO,
panic=abort, and symbol stripping are the price of entry. - Platform API drift. Widgets, App Intents, Live Activities, Material You, and the latest media APIs almost always live outside the Rust core, in native code. Apple and Google ship features faster than any binding generator can chase.
The takeaway for mobile is the same as on the desktop side: Rust as a shared core works. Rust as the whole app on mobile is niche.
And then there is npm
This is the part of the story I do not think gets told enough.
The last 18 months were brutal for the npm registry. Three events worth grounding the rest of this on:
qix) owns some of the most foundational packages in JavaScript: chalk, debug, ansi-styles, strip-ansi, color-convert, and a dozen more. He received a phishing email from support@npmjs.help, a lookalike domain registered three days earlier. The attacker captured his credentials and TOTP through an adversary-in-the-middle session and published malicious versions of 18 packages with combined weekly downloads of roughly 2 billion. Exiger estimated the transitive blast radius at around 34% of the entire npm registry. The payload was a browser-side crypto-clipper that hooked window.ethereum, window.solana, fetch, and XHR, then swapped wallet addresses in outgoing transactions using minimum Levenshtein distance so the substitution would survive a quick visual check. Aikido flagged it within five minutes. The malicious versions were on the registry and being installed by CI worldwide for the entire window before that. Vercel published a same-day advisory.@ctrl/tinycolor (2M+ weekly downloads) was found to contain a self-replicating worm. The malware was the first true worm in the npm ecosystem: every install used the victim's npm token to enumerate other packages the maintainer owned, injected a Webpack-bundled payload, bumped the version, and republished. By the time CISA issued its alert on September 23, more than 500 packages were affected, including ones from CrowdStrike, @nativescript-community, and @operato. The payload ran TruffleHog against the host to scrape AWS keys, GCP credentials, Azure tokens, GitHub PATs, and npm tokens, then uploaded them to public GitHub repos named "Shai-Hulud." A second wave on November 24 moved its hook from postinstall to preinstall (so it ran before any user-visible install output) and hit roughly 25,000 GitHub repositories. CERT/CC VU#534320 called it the first credential-stealing, self-propagating worm in npm.pull_request_target workflow that ran attacker-controlled fork code, GitHub Actions cache poisoning across the fork/base trust boundary, and OIDC token extraction from the runner's process memory. Once inside maintainer CI, the worm did what its predecessor did: scrape credentials, find publishable packages, inject, republish.connect-kit attack that drained DeFi front-ends for about five hours, the LottieFiles compromise in October 2024, the Rspack token theft in December 2024) is what made September 2025 land as a confirmation, not a surprise. May 2026 made it a pattern.preinstall, install, and postinstall hooks run before any human can review them, with full read and write filesystem access and full network. A typical app's node_modules is several thousand packages from several hundred maintainers, any one of whom can phish or token-leak their way into your build. A logger and a color formatter have the same operating-system privileges as your application code, because Node has no capability-based security to draw a line between them.The protective measures that shipped (npm provenance, expanded required 2FA in October and November 2025, scan tools like Socket and OSV-Scanner) help at the margins but do not change the model. A phished maintainer with a valid provenance signature still ships valid signed malware. Mini Shai-Hulud proved that this week, with attestations the GitHub Actions runner generated honestly while compromised. 2FA does not stop an adversary-in-the-middle proxy. Detection time has tightened from days to minutes, which is real progress, but the first thousand installs of a poisoned package still happen.
Why this makes Rust tooling load-bearing
node_modules is still a forest of trust assumptions. What it does change is that the build toolchain (the most-installed, most-privileged surface area in the average project) leaves the npm graph entirely.postinstall. There is no transitive graph. There is a single maintainer organization whose key you can pin.npm install I ran this week:| Toolchain choice | Top-level deps | Total packages | Disk |
|---|---|---|---|
eslint + prettier | 59 | 77 | 20 MB |
@biomejs/biome (Rust) | 1 platform binary wrapper | 1 user-facing | 48 MB binary |
rollup | 3 | 4 | 4.5 MB |
rolldown (Rust) | 3 platform binary wrapper | 4 | 19 MB |
Biome's own docs report that a realistic ESLint + Prettier + typical plugins setup lands closer to 127 to 200 packages once plugin ecosystems get involved. The 59 above is the bare minimum.
serde's precompiled binaries, the occasional malicious crate). What it does not have is a postinstall hook that fires arbitrary code on every developer machine for every transitive build dependency, on every install, forever. That is the difference that matters.Why Rust specifically
Two reasons. The performance numbers are the easy half: 5 to 30x for bundlers, 50 to 100x for linters, 3 to 4x lower memory across the board. That alone would explain the move.
The harder half is everything else.
- Memory safety without a GC. Long-running dev servers and HMR processes do not want to pause for 200ms at the wrong moment. GC-based runtimes (Go, JS) hit this; Rust does not.
- Single-binary distribution. CLIs like
oxlintandbiomeship as one executable per platform. No Node bootstrap, no thousandrequire()calls, no JS launcher script wrapping a native binary wrapping the actual logic. - Fearless parallelism.
rayonandtokiolet linters and bundlers fan out across cores cleanly. The JS event loop is fast but it is one core at a time. - WebAssembly as an escape hatch. Node 25.2 ships SWC as wasm inside core. Browser playgrounds run Oxc directly. The same Rust crate can serve a CLI, a Node API, and a browser sandbox without rewriting any of the hot path.
What I would build with this stack today
If I were starting a new app this week:
next@16orvite@8for the framework. Both Rust bundlers underneath.@biomejs/biomefor formatting plus a lint baseline.oxlintas a CI gate for the type-aware rules.- Tailwind v4 with the Oxide engine.
- A Rust core library for anything cryptographic, anything sync-heavy, or anything I would want to share between web and mobile, exposed through UniFFI or
flutter_rust_bridgeif mobile is on the roadmap. bunorpnpmas the package manager. Lockfile discipline matters more than the manager you pick.
npm create.The honest limitations
Three things I would rather not glaze over.
The contributor bus factor is real. Rust is a learning curve for JS-native maintainers. Concentration on Turbopack, Oxc, and Biome is worth pricing in if you are building on top of these tools.
Memory regressions happen. Rolldown 1.0 RC.18 shipped with about 7x higher RSS than Vite 7 in dev for some apps before the fixes landed. Bundlers in Rust are not automatically more memory-efficient. They are faster, but the wins live in CPU time, not necessarily memory.
Try it yourself
bunx @biomejs/biome init plus bun add -d oxlint will take you 15 minutes. The Biome migration commands (biome migrate eslint, biome migrate prettier) are doing the right thing in 2026. The Vite 8 upgrade is a npm install vite@8 for most apps. Next.js 16 is the same story; the Turbopack flag is just gone now.The faster builds are the part people notice. The smaller dependency tree is the part your security team should notice. The shared Rust core that could ship to mobile next quarter is the part your CTO should notice.
It all started with one team being annoyed enough at webpack to write SWC. We got here by accident.


