← Back to blog

Rust Owns the JavaScript Toolchain in 2026

By Agnel Nieves18 min read
Rust
JavaScript
Web Development
Security
Tooling
Mobile
View as Markdown

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

I noticed it in pieces. I shipped a terminal portfolio over SSH in Rust two weeks ago, and the build pipeline felt like a different planet to anything I had touched in Node years ago. Then Next.js 16 went stable with Turbopack as the default. Then Vite 8 shipped with Rolldown. Then Anthropic merged a Zig-to-Rust port of Bun, mostly written with AI. Then a fresh Mini Shai-Hulud wave hit TanStack, Mistral, and 160+ npm packages three days ago, on the heels of a November Shai-Hulud campaign that compromised roughly 25,000 GitHub repos. "Small dependency" is now a polite phrase for "unsandboxed code with full filesystem access."

Three threads, one direction. Worth writing down.

I am late to this take. Lee Robinson wrote "Rust Is Eating JavaScript" and the 2026 update made the bigger point cleanly. The angles I want to add are mobile and security, because the toolchain story is only the first act.

The toolchain went Rust

Pick a step in the build pipeline and chances are it has been quietly rewritten in Rust.

Turbopack. Next.js 16 (February 2026) shipped Turbopack as the default bundler for both 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 8 shipped on March 12, 2026 with Rolldown as the bundler, taking over from the Rollup plus esbuild combination. Public production numbers from the Rolldown team: Excalidraw dropped from 22.9s to 1.4s. PLAID, an internal product at ByteDance, dropped from 80s to 5s. Both are roughly 16x. The transitional 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.

Rspack. ByteDance's webpack-compatible Rust bundler hit 1.7 in January and is on the runway to 2.0. Internally they ship TikTok, Douyin, Lark, and Coze on it. Externally, Microsoft, Amazon, and Discord are in the public adopter list. The migration cases I have seen all land in the same neighborhood: 9x faster cold dev, 3x less memory, prod builds under 4 seconds on apps that used to take 30.

Biome and oxlint. ESLint is no longer the default lint stack. Biome 2 added type-aware rules that do not require running the TypeScript compiler. Oxlint 1.0 went stable in August, ran 50 to 100x faster than ESLint on the same rulesets, and shipped a type-aware alpha in March on top of 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.

Lightning CSS. Tailwind v4's "Oxide" engine is Lightning CSS underneath. Full builds 5x faster, incremental builds up to 100x. The PostCSS chain is gone from the default setup.

Bun. Bun was a Zig project. In May, Anthropic (which acquired Bun last year and uses it inside Claude Code) merged a port of the entire codebase from Zig to Rust into main. The port is roughly 966,000 lines, it was largely AI-assisted, and it passes 99.8% of the existing test suite on Linux x64 glibc. Bun 1.3.14 is positioned as the last Zig release. The reasons given: Rust's memory safety story, and Zig's no-AI contribution policy clashing with how Anthropic builds tooling.

Deno and Node. Deno's core was always Rust, and the 2.6 release in December bumped V8 to 14.2, added 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.

If you list the build steps in your CI pipeline (lint, format, type check, transpile, bundle, minify, optimize CSS) the only step still being done by JavaScript on JavaScript code in 2026 is type checking. And the most consequential type checker (Microsoft's official one) is being ported to Go, not Rust, by the team that wrote TypeScript in the first place. That is the one part of the ecosystem where the obvious "rewrite it in Rust" bet did not pay off, and it is worth saying out loud.

Bigger than dev tools

This is bigger than JavaScript. Microsoft has publicly committed to embracing Rust across Azure infrastructure, and Azure CTO Mark Russinovich has spent the last two years telling every conference audience that new kernel development at Microsoft should stop using C or C++. New code for Windows and Azure should be written in Rust. That is not a future commitment. Production components already moved: parts of 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.

The Linux kernel has merged Rust drivers since 6.1 (late 2022) and the Rust-for-Linux subsystem keeps growing each release. AWS rewrote Firecracker, its microVM monitor, in Rust years ago and continues to add Rust services across the stack. Google has Rust in Android, in parts of Chromium, and in Fuchsia. When the kernel your build tools run on is moving to Rust, the build tools moving to Rust is the predictable next step.

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_core Rust SDK consumed by every client.

The new bit, and the reason this thread is worth pulling on right now: in December 2024 Mozilla and Filament released Uniffi for React Native. It generates a TurboModule (TypeScript plus the JSI C++ glue) directly from a Rust crate and a UniFFI interface definition. That finally aligns React Native with the pattern Signal and 1Password have been using productively for years. Flutter has had 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 Rust-native UI frameworks (Dioxus, Slint, egui) are real and improving. Slint added an iOS tech preview in 1.12 last June, with proper Xcode integration, simulator and device deploy, TestFlight, and App Store publishing. Dioxus 0.7 runs on iOS reasonably well and on Android with some pain (Huawei and Airbus are public production references). None of these are what I would reach for to build a polished consumer app today, but the trade is no longer "no Rust GUI on phones." It is "yes, but you should know what you are signing up for."

The honest tradeoffs on mobile have not gone away.

  • Toolchain pain. NDK plus cargo-ndk for Android. Xcode, codesigning, provisioning, and a sometimes fragile lipo/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:

September 8, 2025: qix / chalk + debug. A maintainer named Josh Junon (handle 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.

September 15, 2025: Shai-Hulud. A week later, @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.

May 11, 2026: Mini Shai-Hulud. Three days ago, as I write this. A threat actor StepSecurity has been tracking as TeamPCP hit TanStack hard: 42 packages, 84 malicious versions. The same campaign caught Mistral AI, Guardrails AI, UiPath, OpenSearch, and the Bitwarden CLI. Socket flagged 416 affected packages in total; Aikido catalogued 373 malicious package-version entries across 169 names. The technique was novel: a 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.

The detail that mattered most: the malicious TanStack packages were the first documented npm malware shipping with valid SLSA provenance attestations. The cryptographic chain worked perfectly. The attacker had simply stolen the GitHub OIDC token used to sign builds. Provenance proves "this package was built by this CI run." It does not prove the CI run was not compromised.

The earlier baseline (the December 2023 Ledger 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.

The structural reason these keep happening: npm is built on install-time arbitrary code execution and ambient authority. The 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

Moving the build toolchain to Rust does not fix the structural problems in your runtime dependencies. Your app's 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.

A linter, a formatter, a bundler, a test runner, and a transpiler are pure dev-time tools with read access to your whole codebase and write access to your machine. They are also among the most-installed packages on npm, which is exactly what made their maintainers attractive phishing targets. Replacing them with one statically linked Rust binary collapses that surface. There is no postinstall. There is no transitive graph. There is a single maintainer organization whose key you can pin.

Concrete numbers from a clean npm install I ran this week:

Toolchain choiceTop-level depsTotal packagesDisk
eslint + prettier597720 MB
@biomejs/biome (Rust)1 platform binary wrapper1 user-facing48 MB binary
rollup344.5 MB
rolldown (Rust)3 platform binary wrapper419 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.

Cargo has its own supply-chain risks (typo-squats, the long-running debate about 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 oxlint and biome ship as one executable per platform. No Node bootstrap, no thousand require() calls, no JS launcher script wrapping a native binary wrapping the actual logic.
  • Fearless parallelism. rayon and tokio let 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.

It is also worth saying clearly: Rust is not the only winner. TypeScript's compiler is being ported to Go, not Rust, by Anders Hejlsberg and the team that wrote TypeScript in the first place. The 7.0 beta dropped on April 21, 2026 with a 10x speed claim. Bun started in Zig and only just switched. The honest story is "native code ate JS tooling, mostly Rust, but not entirely."

What I would build with this stack today

If I were starting a new app this week:

  • next@16 or vite@8 for the framework. Both Rust bundlers underneath.
  • @biomejs/biome for formatting plus a lint baseline. oxlint as 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_bridge if mobile is on the roadmap.
  • bun or pnpm as the package manager. Lockfile discipline matters more than the manager you pick.

The whole pipeline would have one or two Node processes that exist mainly to host the dev server and run user code. Everything else underneath would be Rust binaries doing the heavy work. That is not aspirational anymore. That is just npm create.

The honest limitations

Three things I would rather not glaze over.

Plugin ecosystems fragment. Every Rust tool eventually faces the same fork: stay in Rust (fast, inaccessible to most JS contributors) or expose a JS plugin API (slower, ergonomic). Rolldown went hard on Rollup-plugin compatibility for adoption. Oxlint shipped a JS plugin alpha in March because too many ESLint rules people care about live in plugin land. Marvin Hagemeister's Speeding up the JavaScript ecosystem series is the canonical write-up of this tension and worth reading if you are considering migrating.

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

If you have not migrated a project off ESLint yet, 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.


Built by Agnel Nieves, a design engineer with 15+ years across product, design systems, and crypto. More writing on the blog.

View as Markdown