Skip to main content

The Problem with JavaScript Framework Benchmarks 2026

·PkgPulse Team
0

TL;DR

"Framework X is 10x faster than React" is almost always comparing toy apps under artificial conditions. The classic JS framework benchmarks (js-framework-benchmark, TodoMVC) test operations like "create 1000 rows" — workloads that don't exist in real apps. Your app performance is 90% determined by: bundle size delivered to users, time-to-interactive, API latency, and how well you avoid unnecessary re-renders. Not which framework you chose. Here's how to benchmark what actually matters.

Key Takeaways

  • Synthetic benchmarks test toy apps — rarely relevant to production workload
  • The DOM is the bottleneck — your JavaScript is not the slow part
  • React is "slow" in benchmarks because it's general-purpose, not because it's bad
  • Solid/Svelte/Qwik are fast in benchmarks — also fast in apps, but not 10x in practice
  • What to actually measure: LCP, INP, bundle size, API latency, perceived performance

What the Famous Benchmarks Actually Test

The js-framework-benchmark (krausest/js-framework-benchmark):
→ The most cited benchmark for framework performance
→ Tests: create 1000 rows, update 10 rows, swap 2 rows, select a row, delete a row
→ Measures: operation time in milliseconds

Results (typical, approximate):
Vanilla JS:    1.0x  (baseline)
Solid:         1.1x  (very close to vanilla)
Svelte:        1.2x
Inferno:       1.1x
Preact:        1.3x
React:         1.8x
Vue 3:         1.7x
Angular:       2.2x

Implication claimed: "React is 80% slower than optimal!"
Actual implication: React adds ~1ms to a 1000-row table render.

The test creates a <table> with 1000 rows.
No real app looks like this.
No user interaction involves 1000 simultaneous DOM mutations.
The benchmark tests the edge case, not the common case.

Why React "Loses" Benchmarks (And Why It Doesn't Matter)

React's design choices that cost benchmark performance:

1. Virtual DOM diffing
   → React builds a JS representation of the DOM, then diffs it
   → Solid/Svelte compile to direct DOM updates (no diffing)
   → Diffing adds overhead for large updates
   → For small updates (typical UI): difference is unmeasurable

2. Generality over specificity
   → React's reconciler handles any component tree update
   → Solid uses fine-grained reactivity (subscribes to specific values)
   → React's approach is more flexible but has more overhead per operation
   → Solid's approach is faster but requires more discipline from the developer

3. Fiber architecture (deliberate trade-off)
   → React's scheduler enables interrupting and resuming rendering
   → This is what enables Suspense, Transitions, useDeferredValue
   → The overhead from React's scheduler is the cost of these features
   → The benchmark doesn't use these features — so it looks "slow"

What the benchmark misses:
→ Bundle size (React's virtual DOM is small; what matters is your app code)
→ Streaming (React Server Components, Suspense)
→ Developer productivity (slower to build = slower to ship)
→ Error handling and edge cases
→ Real user interaction patterns

In practice:
A Solid app and a React app rendering a typical dashboard:
→ Both render in <16ms (60fps threshold)
→ User cannot perceive the difference
→ The benchmark difference is real but imperceptible

What Actually Makes Apps Slow

If your app is slow, it's almost certainly one of these:

1. JavaScript bundle size (most common)
   → 1MB of JS = 10 seconds parse time on a low-end phone
   → The framework is 20-100KB. Your app code is 500KB-2MB.
   → Fix: code splitting, lazy loading, tree shaking
   → Framework choice matters here: Svelte (0KB runtime) < React (45KB) < Angular (150KB)

2. Blocking API calls
   → User clicks "Load data", waits 800ms for API response
   → User perception: "app is slow"
   → Fix: streaming, optimistic updates, server-side rendering
   → Framework choice doesn't affect API latency

3. Render blocking resources
   → CSS in <head> that hasn't loaded yet
   → Fonts that aren't preloaded
   → Third-party scripts that block parsing
   → Fix: <link rel="preload">, lazy script loading, CSP

4. Layout thrashing (JavaScript)
   → Reading layout properties (getBoundingClientRect) then writing
   → Causes multiple reflows
   → Fix: separate reads and writes, use requestAnimationFrame
   → Framework doesn't matter here — it's your component code

5. Unoptimized images
   → 2MB uncompressed PNG where 50KB WebP would do
   → Fix: next/image, astro:image, sharp processing
   → Zero relationship to framework benchmarks

The framework benchmark measures:
→ How fast the framework updates DOM
→ This matters when: you're updating thousands of DOM nodes per second
→ Real apps that do this: stock tickers, data grids, real-time dashboards
→ Real apps that don't do this: ~95% of all apps

How to Actually Benchmark Your App

# Real performance metrics to track:

# 1. Core Web Vitals (what Google measures)
# LCP (Largest Contentful Paint): < 2.5 seconds
# INP (Interaction to Next Paint): < 200ms
# CLS (Cumulative Layout Shift): < 0.1

# Measure with:
npx lighthouse https://your-app.com --view
# Shows all Core Web Vitals + opportunities

# Real device testing (most important):
# Use Chrome DevTools → CPU throttling 4x + Network slow 3G
# Test on actual low-end Android device
# This reveals what your framework benchmark score never shows

# 2. JavaScript bundle size (biggest lever)
# Build your app, analyze the bundle:

# Vite:
ANALYZE=true npx vite build
# Or add rollup-plugin-visualizer:
npm install -D rollup-plugin-visualizer
# Add to vite.config.ts plugins, run build, open stats.html

# Next.js:
ANALYZE=true npx next build
# (install @next/bundle-analyzer first)

# What you'll find:
# → Your "framework" is probably 20-50KB
# → Your date library (if not tree-shaken) is 300KB
# → A chart library you use on one page loads on all pages
# → This is where to focus, not framework choice

# 3. Measure actual frame rate during interaction
# Chrome DevTools → Performance → Record while interacting
# Look for: long tasks (>50ms), layout recalculations
# A "slow" React component that runs in 2ms is not your problem

The Benchmark That Actually Matters for Framework Choice

// The real benchmark: developer productivity

// Question: How long does it take to build a feature?

// React — feature development time: ~1x (baseline, large ecosystem)
// → Lots of libraries, lots of examples, lots of Stack Overflow answers
// → Type system with TypeScript is excellent
// → Server Components reduce client JS significantly

// Svelte — feature development time: ~0.9x (slightly faster)
// → Less boilerplate: no useState, no useEffect boilerplate
// → But: fewer examples, smaller ecosystem, need to learn more yourself

// Vue — feature development time: ~0.9x
// → Options API is beginner-friendly
// → Composition API is excellent TypeScript
// → Strong in Asia (huge community), growing globally

// Solid — feature development time: ~1.2x
// → Fine-grained reactivity requires understanding how it works
// → Fewer libraries, smaller community
// → Worth it? Only if you're hitting React's performance limits

// The honest comparison:
// If you're hitting the js-framework-benchmark's use case in production,
// Solid/Svelte are genuinely better choices.
// If you're not (95% of apps), React's ecosystem advantage > performance gap.

// "But React is 1.8x slower in the benchmark!"
// At 60fps, you have 16ms per frame.
// React processes typical UI in 1-3ms.
// Solid processes the same UI in 0.5-1.5ms.
// Users cannot perceive the difference.

The One Benchmark Worth Paying Attention To

Bundle size is the benchmark that maps directly to user experience:

Initial JS bundle size → Time to Interactive on real devices
(Because JS must be downloaded, parsed, AND executed before the app is interactive)

Gzipped JS bundle (framework only):
Svelte: ~2KB runtime (compiles to vanilla JS)
Solid: ~7KB
Preact: ~4KB
Vue 3: ~33KB
React + ReactDOM: ~45KB
Angular: ~150KB

In a real app: these numbers are dominated by your application code.
A medium React app is typically:
→ React: 45KB
→ Your application code: 200-500KB
→ Libraries: 50-200KB

Total: 300-750KB
Framework: ~6% of total bundle

Switching from React to Svelte saves ~45KB.
Cutting lodash saves ~70KB.
Lazy-loading one heavy route saves ~100KB.

The framework's bundle contribution is real but not the biggest lever.
Code splitting your routes has 2-5x more impact than switching frameworks.

The benchmark worth running: npm run build, check dist/ sizes,
look at route-level code splitting, look at what libraries you're bundling.
That's where to optimize. Not "did I pick Solid over React?"

What Real-World Performance Looks Like in 2026 Apps

What actually determines performance in modern JavaScript applications has very little to do with the framework's render engine. In nearly every production audit, the dominant contributors to slow user experience fall into four categories that benchmarks don't touch.

Bundle size and code-splitting remain the single biggest performance lever. Shipping less JavaScript matters far more than how efficiently a framework updates the DOM. A 50KB Svelte bundle outperforms a 200KB React bundle on a mid-range Android phone in Southeast Asia — but that is a bundle size difference, not a reactivity algorithm difference. A React app with aggressive route-level code splitting will consistently beat a Svelte app that lazy-loads nothing.

Server-side rendering and streaming has become the defining architectural split between fast and slow apps. Next.js, Nuxt, and SvelteKit all implement React's streaming SSR (or their own equivalent), which dramatically improves Largest Contentful Paint by sending meaningful HTML before JavaScript executes. A framework's SSR implementation and streaming support matters orders of magnitude more for perceived performance than client-side rendering throughput benchmarks.

Image optimization is responsible for more Core Web Vitals failures in the wild than all JavaScript framework overhead combined. An unoptimized 2MB hero image, a missing loading="lazy" attribute, or a JPEG where WebP would serve — these issues register immediately in field data. No benchmark tests this because no benchmark loads real assets.

Database and API response time is what users actually feel in full-stack applications. When a user perceives an app as slow, they're usually waiting for a network response, not for 1,000 DOM nodes to re-render. The framework benchmark measures milliseconds of rendering that users cannot consciously detect, while a 600ms API response is felt as friction on every page load.

The implication: if you're choosing a framework for a new project in 2026, benchmark criteria should be SSR quality, bundle splitting defaults, and ecosystem support for your data-fetching patterns — not which framework wins a synthetic row-update test.


The Only Framework Benchmark That Actually Matters

There is one real-world benchmark worth looking at seriously: the RealWorld App benchmark at github.com/gothinkster/realworld. It implements the same application specification — a Medium-like blogging platform with authentication, CRUD articles, comments, and user profiles — in every major framework. Unlike the js-framework-benchmark, RealWorld shows actual bundle sizes, actual hydration costs, actual architecture patterns, and actual maintenance realities.

The caveat is that RealWorld tests a specific application type (a CRUD web app with moderate interactivity) that may not match your use case. A data grid application, a real-time collaboration tool, and a marketing site with some interactive components each have different performance profiles that no single benchmark captures.

The most useful benchmark you can run is a prototype of your own use case. Pick the two or three frameworks you're seriously considering, implement the same representative feature in each — the part of your app that will be most performance-sensitive — and measure it. The question to answer is not "which framework is faster in the abstract?" but "can this framework's default patterns meet my performance budget for my specific problem?"

A framework that struggles with your dominant pattern — whether that's server rendering a large data set, rendering a real-time feed, or handling a complex form with many interdependencies — will hurt production performance regardless of how it performs on generic benchmarks. A framework that excels at your dominant pattern will feel fast even if it loses the js-framework-benchmark.

The meta-lesson from every team that has measured real user performance in production is consistent: architecture decisions dominate. How you fetch data, how you split code, whether you SSR and when — these choices account for the vast majority of the performance gap between fast and slow apps. Framework choice, holding architecture constant, is rounding error.


The TodoMVC Problem: Benchmarks Test Trivial Apps

TodoMVC started as a useful reference application for comparing framework syntax — a simple to-do list implemented in every framework so developers could see what idiomatic code looks like. What it was never designed to be is a performance test. The operations involved in a to-do app — adding an item, marking it complete, filtering the list, deleting an entry — involve at most a handful of DOM mutations on a small array. These operations are so lightweight that the performance differences between frameworks are measured in microseconds.

The js-framework-benchmark amplifies this by scaling up the problem artificially: instead of a realistic to-do list with 20 items, it tests with 1,000 rows and measures how fast each framework can create, update, swap, and delete them in bulk. This does surface real algorithmic differences in how each framework handles the DOM. Solid's fine-grained reactivity is genuinely more efficient than React's virtual DOM diffing for this specific pattern. But the gap between "genuinely more efficient at 1,000 row bulk operations" and "will make your app feel faster to users" is enormous, and the benchmark discourages reflection on that gap.

Real applications do not perform 1,000 simultaneous DOM mutations in user-facing interactions. A data grid that renders 1,000 rows does not update all 1,000 simultaneously — it updates the row the user interacted with, or the column that was sorted, which represents a small delta. The frameworks that benchmark worst on bulk operations frequently handle incremental updates with identical performance to frameworks that benchmark best. The benchmark specifically selects for the case that makes the performance gap look largest, not the case that represents what applications actually do.

The framing problem compounds the issue. When a benchmark result is reported as "Solid is 1.8x faster than React," it implies a general performance superiority that does not exist. Solid processes a 1,000-row bulk update in roughly half the time React does. Both complete in milliseconds. On the incremental interactions that constitute 99% of real usage, the difference is below 1 millisecond. The headline misrepresents what the data actually shows.

SSR Benchmarks and the Hidden Cost of Hydration

Server-side rendering benchmarks present a different kind of misleading picture. SSR throughput — how many HTML responses per second a framework can render on the server — is a legitimate performance concern for high-traffic applications. Solid, Svelte, and Preact all have faster SSR throughput than React in controlled benchmarks because their server rendering paths are simpler. This is a real difference with real implications at scale.

What SSR benchmarks almost universally fail to test is hydration — the client-side process of making server-rendered HTML interactive by attaching JavaScript event handlers and reconciling the server-rendered state with the client-side application. Hydration is typically the slowest phase of the initial page load experience, and it happens after the user sees the page but before they can interact with it. A framework that produces fast server HTML but has slow hydration will feel slower to users than a framework with slower server rendering but faster hydration, because hydration determines the Interaction to Next Paint metric — the delay between a user's first click and the application responding.

React's solution to hydration overhead is selective hydration via Suspense and, more significantly, React Server Components, which eliminate hydration entirely for components that don't require client-side interactivity. A React Server Component sends HTML with no corresponding client JavaScript bundle — there is nothing to hydrate because the component has no client-side existence. This architectural approach trades SSR throughput for hydration elimination, which is a reasonable trade for most applications. Qwik takes the most aggressive approach to this problem with resumability: serializing the entire application state into the server-rendered HTML so the client JavaScript never needs to replay server work to become interactive. The hydration-free model is genuinely superior in theory; its practical constraints are ecosystem maturity and debugging complexity.

The SSR performance story in 2026 is therefore: React has slower raw SSR throughput but progressive hydration and RSC reduce the client-side cost that SSR throughput benchmarks don't measure. Solid and Svelte have faster raw SSR throughput but still require hydration for interactive components in most configurations. Neither picture is captured by straightforward "responses per second" benchmarks.

What Benchmarks Don't Measure: DX, Ecosystem, and Hiring

A fair accounting of framework selection criteria must acknowledge that raw runtime performance is one factor among many, and often not the most important one for the majority of applications. The factors that determine whether a team ships a reliable product on schedule are largely invisible to benchmarks: developer experience, ecosystem breadth, hiring pool, documentation quality, and community support for debugging unusual problems.

React's ecosystem depth — the sheer number of libraries, components, templates, and Stack Overflow answers — is a compounding advantage that does not appear in any performance table. When a developer encounters an unusual authentication edge case, a complex animation requirement, or a data visualization need, React's ecosystem is five to ten times larger than Svelte's and several times larger than Vue's. The time saved by finding a maintained library that solves the problem, rather than building it from scratch, dwarfs any framework-level rendering performance difference.

Hiring is a concrete cost that benchmark comparisons ignore entirely. If a team is at ten engineers, the available pool of developers with React experience is an order of magnitude larger than the pool with Svelte or Solid experience. Training new developers in an unfamiliar framework takes weeks and introduces risk during the learning period. For teams that need to scale headcount, React's ubiquity in the hiring market is a material advantage that a 1.8x synthetic benchmark performance gap cannot offset.

Developer experience affects software quality in ways that are hard to measure but very real. A framework that has excellent TypeScript integration, fast hot reload, clear error messages, and strong testing patterns reduces the rate of bugs shipped to production. It allows developers to focus on application logic rather than framework ceremonies. Svelte's consistently high developer satisfaction scores — 74% positive in the State of JS survey — are not just aesthetic preference; satisfied developers write better code with fewer defects and iterate faster.

The Frameworks That Benchmark Well but Ship Slowly

There is a pattern in the JavaScript framework ecosystem of frameworks that achieve excellent benchmark scores but have slower feature development velocity or ecosystem maturity that limits their practical applicability. This is not a criticism — the trade-offs are often intentional — but it is worth naming as a selection consideration.

Inferno is a canonical example: it consistently ranks near the top of the js-framework-benchmark, outperforming every framework except Solid, and it is largely unknown outside of performance-conscious niches because its development pace is slow and its ecosystem thin. Inferno is an excellent technical achievement that demonstrates React-compatible APIs can be implemented with minimal overhead. It is not a practical choice for a production application that needs third-party component libraries, routing solutions, and active community support.

Solid occupies a different position — it is growing and has genuine community momentum — but it shares the characteristic that its ecosystem significantly lags React's. The libraries that exist for Solid are well-maintained and the core team is active, but the coverage gap means developers on Solid will hit cases where they need to build or adapt tooling that React developers can import from npm. This is an acceptable trade for teams where the Solid performance model genuinely addresses a production bottleneck. It is not an acceptable trade for teams that choose Solid primarily because a benchmark said it was faster.

The practical lesson from frameworks that benchmark exceptionally well is to ask not just "how fast is this?" but "what is the ecosystem cost of this approach, and does my application actually operate in the performance regime where this speed difference is perceptible?" Most applications do not. The benchmark winners make interesting reading; they are not automatically the right production choice.

How the JS Community's Benchmark Culture Creates Hype Cycles

The JavaScript ecosystem has an established pattern of benchmark-driven hype cycles that follows a consistent arc. A new framework or runtime appears with compelling benchmark numbers. Early adopters publish those numbers widely. Articles appear comparing framework X to the incumbents with headline percentages that imply revolutionary performance gains. The framework sees rapid growth in downloads and GitHub stars. Developers adopt it for production use cases at scale. Real-world limitations — ecosystem gaps, debugging complexity, edge cases not covered by the benchmark — surface in production. Adoption stabilizes at a fraction of the initial hype, with a loyal community of users for whom the trade-offs genuinely make sense.

This cycle is not inherently bad — it is how new ideas get evaluated and refined. Solid's benchmark numbers attracted attention that led to the signals model being taken seriously and eventually adopted by mainstream frameworks. Qwik's resumability concept attracted attention through performance claims that led to genuine innovation in hydration architecture. The hype served a function even when the specific adoption outcome was narrower than the hype implied.

The dysfunction enters when benchmark-driven hype leads teams to make architectural decisions that are hard to reverse. Rewriting an application from React to a new framework because a benchmark suggested a 10x performance improvement, only to find the improvement is 1.3x under real conditions and the ecosystem gaps add weeks of development overhead, is a costly mistake that benchmark culture enables. The antidote is not to ignore benchmarks but to treat them as the narrow hypothesis-generators they are, then test the hypothesis on your actual application before committing to an architectural change.

In 2026, the benchmark landscape has matured somewhat — fewer developers take synthetic benchmarks at face value, and the conversation around real-world performance testing has become more sophisticated. Core Web Vitals from Google's field data, production profiling, and real-user monitoring are increasingly the reference points developers cite rather than js-framework-benchmark scores. That shift reflects a healthier ecosystem understanding of what performance measurements actually mean for user experience.

Compare React, Solid, Svelte, and other frameworks on bundle size and health at PkgPulse.

See also: React vs Vue and React vs Svelte, React 19 Features Every Developer Should Know.

The 2026 JavaScript Stack Cheatsheet

One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly.