React 19 Compiler vs Svelte 5 Compiler
TL;DR
React Compiler and Svelte 5 Runes solve the same problem — unnecessary re-renders — with completely different philosophies. React Compiler automatically adds useMemo/useCallback at build time, so you write React normally and get performance for free. Svelte 5 Runes make reactivity explicit with $state, $derived, $effect — a new reactive primitive system that's more explicit but cleaner than hooks. React's approach requires less learning (same React, just faster). Svelte's approach requires re-learning reactivity primitives but delivers smaller bundles.
Key Takeaways
- React Compiler: opt-in, automatic memoization, works with existing React code, ~15-30% re-render reduction
- Svelte 5 Runes: new primitive system,
$state/$derived/$effectreplacelet+$:, 15-20% smaller bundles than Svelte 4 - Downloads: React 25M/week vs Svelte 1.8M/week — React dominates but Svelte growth is strong
- Learning curve: React Compiler = zero (same code), Svelte 5 = rewrite reactivity model
- Bundle size: Svelte 5 still wins (~5KB runtime vs React's ~46KB), React Compiler doesn't change bundle size
React Downloads vs Svelte Downloads
| Package | Weekly Downloads | Trend |
|---|---|---|
react | ~25M | → Stable dominant |
react-dom | ~24M | → Stable |
svelte | ~1.8M | ↑ Growing |
@sveltejs/kit | ~850K | ↑ Growing |
React's scale advantage is enormous — but Svelte's growth is real.
What Each Compiler Actually Does
The term "compiler" covers two fundamentally different optimization strategies here, and understanding the distinction is essential to evaluating each tool.
The React Compiler is a static analysis tool that runs at build time. It reads your React component code and inserts useMemo, useCallback, and React.memo calls in the right places automatically. You write the same React code you always have. The compiler identifies which values are stable across renders, which computations are expensive, and which function references need to be preserved. The output is still React — it runs on the React runtime, uses the virtual DOM, and behaves exactly as it would if you had written the memoization yourself.
The Svelte 5 Compiler does something more radical. Svelte components are not valid JavaScript — they are a custom file format with .svelte extension that the compiler transforms into vanilla JavaScript. The compiler reads your $state(), $derived(), and $effect() Rune calls, analyzes their dependencies, and generates direct DOM manipulation code that updates only the specific elements that need to change. There is no virtual DOM. There is no reconciliation step. The output is standalone JavaScript that does not need a framework runtime to function.
These are fundamentally different optimization strategies: React Compiler reduces the cost of React's existing reconciliation approach, while Svelte's compiler eliminates reconciliation entirely in favor of fine-grained DOM updates.
React Compiler: Opt-In and Adoption
React Compiler shipped as an opt-in Babel/Vite plugin with React 19. Existing React code does not require changes — the compiler analyzes your components and applies memoization where it identifies benefit. Components that break React's rules (mutations during render, effects without cleanup) are skipped rather than miscompiled.
npm install babel-plugin-react-compiler
// babel.config.js — enable globally
module.exports = {
plugins: [
['babel-plugin-react-compiler', {
compilationMode: 'infer', // Compile all rule-following components
// 'annotation' mode: only compile components with 'use memo' directive
}],
],
};
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [
react({
babel: {
plugins: [['babel-plugin-react-compiler']],
},
}),
],
});
// next.config.ts — Next.js 15+ with React Compiler
const nextConfig = {
experimental: {
reactCompiler: true,
},
};
You can opt individual components out using the "use no memo" directive — useful if a component relies on mutation patterns the compiler doesn't handle correctly:
function LegacyChart({ data }) {
'use no memo'; // Skip compiler for this component
// Component with imperative D3 mutations that would confuse the compiler
return <canvas ref={canvasRef} />;
}
The opt-in model means you can enable React Compiler on an existing codebase incrementally. Start with compilationMode: 'annotation' to require explicit opt-in, validate it works on specific components, then switch to compilationMode: 'infer' for global coverage.
React Compiler: Auto-Memoization
The compiler analyzes your component code and automatically inserts useMemo/useCallback/React.memo where needed:
// You write this:
function ProductCard({ product, onAddToCart }: Props) {
const discountedPrice = product.price * (1 - product.discount);
return (
<div>
<h2>{product.name}</h2>
<p>${discountedPrice.toFixed(2)}</p>
<button onClick={() => onAddToCart(product.id)}>Add to Cart</button>
</div>
);
}
// React Compiler transforms to roughly:
function ProductCard({ product, onAddToCart }: Props) {
const discountedPrice = useMemo(
() => product.price * (1 - product.discount),
[product.price, product.discount]
);
const handleAddToCart = useCallback(
() => onAddToCart(product.id),
[onAddToCart, product.id]
);
return (
<div>
<h2>{product.name}</h2>
<p>${discountedPrice.toFixed(2)}</p>
<button onClick={handleAddToCart}>Add to Cart</button>
</div>
);
}
The transformation is more sophisticated than this illustration — it uses fine-grained dependency tracking and only memoizes when the analysis determines it is beneficial. Components are not blindly wrapped in React.memo; the compiler evaluates each value and function reference individually.
Svelte 5 Runes: New Reactivity Primitives
Svelte 5 introduced Runes as a replacement for Svelte 4's implicit reactivity model. In Svelte 4, top-level let declarations were automatically reactive, and $: prefixed statements were reactive computations. This worked but was implicit — it was not clear from syntax alone which variables were reactive.
Svelte 5 makes reactivity explicit with Rune syntax. The $ prefix is no longer syntax sugar for the compiler; it is a set of well-defined primitives with clear semantics.
<!-- Svelte 4 (old): -->
<script>
let count = 0; // Reactive by default (implicit)
$: doubled = count * 2; // Reactive derived value (implicit)
$: console.log(count); // Reactive side effect (implicit)
</script>
<button on:click={() => count++}>{count} × 2 = {doubled}</button>
<!-- Svelte 5 (new Runes): -->
<script>
let count = $state(0); // Explicit reactive state
let doubled = $derived(count * 2); // Explicit derived value
$effect(() => { console.log(count); }); // Explicit side effect
</script>
<button onclick={() => count++}>{count} × 2 = {doubled}</button>
The three core Runes map to familiar concepts: $state() is reactive state (replaces reactive let), $derived() is a computed value that updates when its dependencies change (replaces $: computations), and $effect() is a side effect that runs when dependencies change (replaces onMount/$: side effects).
Runes also work in regular .ts files — not just .svelte components — enabling reactive state in class instances and utility modules:
// cart.svelte.ts — reactive state in a plain TypeScript file
export class CartStore {
items = $state<CartItem[]>([]);
total = $derived(
this.items.reduce((sum, item) => sum + item.price * item.quantity, 0)
);
addItem(item: CartItem) {
this.items = [...this.items, item];
}
removeItem(id: string) {
this.items = this.items.filter(i => i.id !== id);
}
}
export const cart = new CartStore();
<!-- Component consuming class state: -->
<script>
import { cart } from '$lib/cart.svelte.ts';
</script>
{#each cart.items as item}
<div>{item.name} — ${item.price}</div>
{/each}
<p>Total: ${cart.total}</p>
<button onclick={() => cart.addItem(newItem)}>Add</button>
This pattern — reactive state living in plain TypeScript rather than only in components — is one of Svelte 5's biggest ergonomic improvements over Svelte 4.
Bundle Size Reality
React Compiler does not reduce bundle size. The transformation inserts additional memoization calls, which can slightly increase bundle size while reducing runtime computation. The React runtime (~46KB gzipped) is unchanged.
Svelte compiles to vanilla JavaScript with a minimal runtime (~5KB gzipped). The compiled output is larger per component than React's equivalent component (because the DOM update logic is inlined rather than delegated to a shared runtime), but the starting baseline is 40KB smaller.
Runtime baseline:
React + ReactDOM: ~46KB gzipped
Svelte runtime: ~5KB gzipped (Svelte 5)
Per-component overhead:
React (simple button): ~0.5KB (behavior in shared runtime)
Svelte (simple button): ~1.2KB (DOM logic compiled into component)
Crossover points:
At ~20 components:
React: 46KB base + ~10KB = ~56KB total
Svelte: 5KB base + ~24KB = ~29KB total ← Svelte wins
At ~50 components:
React: 46KB base + ~25KB = ~71KB total
Svelte: 5KB base + ~60KB = ~65KB total ← Near parity
At ~200 components:
React: 46KB base + ~100KB = ~146KB total
Svelte: 5KB base + ~240KB = ~245KB total ← React wins
The crossover point is around 50 components. Small-to-medium applications — marketing sites, content-driven apps, landing pages, interactive dashboards — typically fall well below this threshold. Large enterprise applications with hundreds of components trend toward React's favor on bundle size.
Re-render Performance
React Compiler reduces unnecessary re-renders by approximately 15-30% according to Meta's published benchmarks on their production apps. The exact improvement depends heavily on how much unnecessary memoization was missing before — codebases that already use useMemo and useCallback extensively will see less improvement; codebases that never memoize will see more.
Svelte 5 avoids a virtual DOM entirely. When count changes, Svelte's compiled output directly sets the text node in the DOM. There is no diffing step, no reconciliation, no fiber traversal. For fine-grained updates in complex interactive UIs, this is fundamentally more efficient — though the practical difference in user-perceptible performance is small for most applications.
The architectures differ enough that direct performance benchmarks are somewhat misleading. React Compiler makes React competitive in the re-render benchmarks that previously favored Svelte. For the vast majority of production applications, both produce a result that users experience as instant.
Package Health
| Package | Weekly Downloads | Maintainer | Status |
|---|---|---|---|
react | ~25M | Meta + community | Active, React 19 stable |
svelte | ~1.8M | Rich Harris (Vercel) + community | Active, Svelte 5 stable |
React's ecosystem scale is orders of magnitude larger — component libraries, form solutions, data fetching, state management, testing tooling, and developer tooling all have React as their primary target. Svelte's ecosystem is smaller but growing; SvelteKit is a strong full-stack framework, and the Svelte community builds high-quality official libraries for most common needs.
Rich Harris joined Vercel in 2021, which has accelerated Svelte's development and given it organizational backing comparable in resources to Meta's React team. Both projects are well-funded, actively maintained, and have stable release cadences. React's 25M weekly download advantage reflects ecosystem entrenchment as much as technical superiority — Svelte is not a niche experiment but a mature, production-ready framework used by thousands of teams in production worldwide.
When to Choose
Choose React 19 Compiler when:
- You have an existing React codebase and want performance gains without a rewrite
- Your team is already proficient in React
- You need the full React ecosystem: server components, vast component library selection, Next.js
- You're hiring and React familiarity is important for onboarding speed
- Large application with 100+ components where bundle size crossover favors React
Choose Svelte 5 when:
- Starting a new project where bundle size is a priority (marketing sites, content apps)
- Developer experience and explicit reactivity appeal to your team
- Smaller team comfortable with a less mainstream ecosystem
- Application scope is small-to-medium where Svelte's runtime advantage holds
- You're building interactive islands where minimal JavaScript is important
For full framework comparison data and download trends, see the Next.js vs SvelteKit comparison on PkgPulse. If you're evaluating full-stack framework options in depth, the Next.js vs SvelteKit 2026 article covers routing, SSR, deployment, and team fit considerations. Track Svelte's download growth trajectory on the Svelte package page.