Published on Medium
| Category | Winner | Runner-up |
|---|---|---|
| Initial Load (FCP) | Angular (80ms) | Solid (92ms) |
| Bundle Size | Solid (257KB) | Lit (265KB) |
| Filter Performance | Solid (78ms) | React (92ms) |
| Clear Filters | Solid (208ms) | React (355ms) |
| Navigate to Detail | Solid (170ms) | Lit (170ms) |
| Back to List | React (197ms) | Solid (281ms) |
| Pagination Cycle | Solid (1,490ms) | Svelte (1,543ms) |
| Detail Navigation | Solid (331ms) | React (336ms) |
| Overall | SolidJS | React |
- Key Findings
- Overview
- Methodology
- Application Design
- Performance Scenarios
- Results
- Limitations
- Resources
A performance comparison of five modern frontend frameworks (December 2025).
I was disappointed to find limited benchmarks comparing frontend frameworks in scenarios that reflect real business applications—specifically, applications with large numbers of components and client-side routing.
To address this gap, I built a custom "Food Facts" application. It uses pre-generated data served by a local backend, ensuring the data order is deterministic and network variability doesn't skew results. This setup provides realistic HTTP fetching behavior while isolating framework performance.
| Framework | Version | Build Tool | Router |
|---|---|---|---|
| Angular | 21.0.3 | Angular CLI / esbuild | @angular/router |
| React | 19.2.1 | Vite 7.1.7 | react-router-dom 7.3.2 |
| Svelte | 5.33.0 | SvelteKit 2.21.0 / Vite 6.3.0 | SvelteKit routing |
| SolidJS | 1.9.10 | Vite 7.2.4 | @solidjs/router 0.15.3 |
| Lit | 3.2.0 | Vite 7.2.4 | @lit-labs/router 0.1.3 |
All projects use TypeScript ~5.9.3 and share types via a workspace package (shared-types).
- Angular: Still a popular choice for enterprise applications. I've worked with Angular for the last 7 years, and performance challenges with large component pages inspired this comparison.
- React: The most popular frontend framework worldwide, making it an essential benchmark.
- Svelte: A trending framework generating buzz for its lightweight, reactive approach.
- SolidJS: A newer library with fine-grained reactivity and good developer experience.
- Lit: A web standards baseline. I considered vanilla JS or raw web components, but implementation variations would affect results. Lit provides a minimal abstraction over web component standards, offering a comparison point closer to the W3C specifications. Also used by Google for projects like Material 3.
Svelte, SolidJS, and Lit can all compile to web components—Svelte and Lit natively, and SolidJS via the solid-element package. This makes them viable options for writing high-performance reusable components that can be embedded in larger frameworks like Angular, offering a potential optimization path for enterprises that cannot afford a full rewrite.
- Machine: MacBook Pro 15-inch, 2018
- Processor: 2.6 GHz 6-Core Intel Core i7
- Memory: 16 GB 2400 MHz DDR4
- Graphics: Intel UHD Graphics 630 1536 MB
- OS: macOS Sequoia 15.7.2
- Browser: Chromium (via Puppeteer)
- Automation: Puppeteer-based test runner
- Runs per scenario: 5 runs per framework per scenario
- Metrics reported: Median values (to reduce outlier impact)
- Backend: Local Node.js server serving cached JSON data
- Network: Localhost (minimal network latency)
Each framework was tested sequentially with the same test scenarios. The test runner waits for network idle and specific DOM elements before measuring to ensure consistent comparison points.
Each framework implementation shares identical HTML structure and CSS styling as much as possible. The application consists of two pages: a list view and a detail view.
The initial page is a list view with 54 columns and 50 rows, rendering ~1,600 components on screen. The columns use 16 different component types to render various data formats:
| Component Type | Description | Example Columns |
|---|---|---|
simple-text |
Plain text display | Product Name, Brand, Category, SKU |
truncated-text |
Expandable text with "show more" | Description |
product-link |
Clickable link to detail page | Code |
progress-bar |
Visual percentage bar | Quality Score, Eco Score |
grade-badge |
Letter grade (A-F) badge | Grade |
nova-dots |
1-4 dot rating display | Safety Rating |
star-rating |
5-star rating component | Customer Rating |
large-counter |
Formatted number display | Stock, Units Sold, Reviews |
decimal-units |
Number with unit suffix | Price, Weight, Tax Rate |
absolute-date |
Formatted date | Created, Release Date |
relative-date |
"X days ago" format | Last Updated |
time-format |
Time display (HH:MM) | Departure Time |
boolean-yesno |
Yes/No badge | In Stock, Featured, Best Seller |
product-image |
Thumbnail image | Image |
color-pill |
Color swatch with name | Color |
The list supports:
- Filtering: Text search, range sliders, and multi-select dropdowns
- Pagination: Navigate through pages of 50 items
- Detail navigation: Click product code to view details
Clicking a product code navigates to the detail page, which displays the same data fields in a different layout with a larger image—rendering only ~35 components.
Component counts measured using Angular with lifecycle instrumentation. All frameworks use identical HTML structure, so counts are consistent across implementations.
The detail page includes:
- Previous/Next navigation: Move between products without returning to the list
- Back to list: Return to the list view
- Initial page load
- Purpose: Tests bootstrapping and load times
- Metrics: First Contentful Paint (FCP), Largest Contentful Paint (LCP), Time to Interactive (TTI), Total Blocking Time (TBT), bundle size
-
Filter application
- Purpose: Hybrid test for component creation, destruction, and change detection
- Action: Apply a text filter that reduces 50 rows to 11 rows
-
Clear filters
- Purpose: Tests component creation time when restoring full dataset
- Action: Clear all filters, restoring from 11 rows back to 50 rows
-
Expand/collapse description
- Purpose: Tests minimal reactive change on a complex page
- Action: Expand truncated text, then collapse it
-
Pagination cycle
- Purpose: Tests component re-rendering with data changes
- Action: Navigate through 4 page transitions (next, next, previous, previous)
- Note: Components are tracked by index, ensuring updates rather than full recreation
-
Navigate to detail
- Purpose: Tests destroying many components and creating fewer
- Action: Click product link to navigate from list (~1,600 components) to detail (~35 components)
-
Navigate back to list
- Purpose: Tests list view creation without cold-start overhead
- Action: Navigate from detail back to list view
-
Detail navigation cycle
- Purpose: Tests component updates without recreation
- Action: Navigate between detail pages using next/previous buttons
- Build metrics
- Purpose: Measure non-runtime performance characteristics
- Metrics: Bundle size, number of requests, largest request size
| Framework | FCP (ms) | LCP (ms) | TTI (ms) | Bundle Size (KB) |
|---|---|---|---|---|
| Angular | 80 | 80 | 10 | 501 |
| React | 104 | 104 | 11 | 451 |
| Svelte | 96 | 96 | 13 | 307 |
| SolidJS | 92 | 92 | 12 | 257 |
| Lit | 100 | 100 | 13 | 265 |
Winner: Angular (fastest FCP/LCP) | Best Bundle: SolidJS (257KB)
| Framework | Filter Duration (ms) | JS Execution (ms) |
|---|---|---|
| Angular | 112 | 112 |
| React | 92 | 92 |
| Svelte | 122 | 122 |
| SolidJS | 78 | 78 |
| Lit | 131 | 131 |
Winner: SolidJS (78ms) - 15% faster than React
| Framework | Clear Duration (ms) | Restored Rows |
|---|---|---|
| Angular | 677 | 50 |
| React | 355 | 50 |
| Svelte | 442 | 50 |
| SolidJS | 208 | 50 |
| Lit | 460 | 50 |
Winner: SolidJS (208ms) - 41% faster than React, 69% faster than Angular
| Framework | Expand (ms) | Collapse (ms) |
|---|---|---|
| Angular | 107 | 105 |
| React | 104 | 104 |
| Svelte | 106 | 106 |
| SolidJS | 106 | 105 |
| Lit | 107 | 108 |
Winner: React (tied with others) - All frameworks perform similarly for minimal changes
| Framework | Navigation Time (ms) | Detail LCP (ms) |
|---|---|---|
| Angular | 190 | 84 |
| React | 171 | 100 |
| Svelte | 187 | 92 |
| SolidJS | 170 | 92 |
| Lit | 170 | 86 |
Winner: SolidJS/Lit (tied at 170ms)
| Framework | Back Navigation (ms) |
|---|---|
| Angular | 1,634 |
| React | 197 |
| Svelte | 740 |
| SolidJS | 281 |
| Lit | 339 |
Winner: React (197ms) — 8x faster than Angular, 2.7x faster than Svelte
This is the most striking result. Chrome DevTools performance profiling revealed Angular's slower times are due to significantly longer script execution during component recreation. React's functional component model may contribute to its advantage here, potentially reducing object creation overhead compared to class-based approaches.
Note: This test navigates through 4 page transitions (1→2→3→2→1). "Total Cycle" is the cumulative time for all 4 transitions.
| Framework | Total Cycle (ms) | Avg Page Transition (ms) |
|---|---|---|
| Angular | 1,675 | 333 |
| React | 1,571 | 301 |
| Svelte | 1,543 | 307 |
| SolidJS | 1,490 | 277 |
| Lit | 1,886 | 360 |
Winner: SolidJS (1,490ms total, 277ms avg per page)
Note: This test navigates between detail pages (1→2→1). "Total Cycle" is the cumulative time for both navigations.
| Framework | Total Cycle (ms) | Avg Navigation (ms) |
|---|---|---|
| Angular | 348 | 17 |
| React | 336 | 12 |
| Svelte | 337 | 13 |
| SolidJS | 331 | 10 |
| Lit | 340 | 15 |
Winner: SolidJS (331ms total, 10ms avg navigation)
| Framework | Total Bytes | Largest Request | Request Count |
|---|---|---|---|
| Angular | 501 KB | 253 KB | 57 |
| React | 451 KB | 262 KB | 54 |
| Svelte | 307 KB | 69 KB | 75 |
| SolidJS | 257 KB | 71 KB | 54 |
| Lit | 265 KB | 91 KB | 54 |
Winner: SolidJS (257KB) - 49% smaller than Angular, 43% smaller than React
| Framework | Wins | Notable Strengths | Notable Weaknesses |
|---|---|---|---|
| SolidJS | 6 | Fastest filtering, smallest bundle, best pagination | Slightly slower initial FCP |
| React | 2 | Excellent back navigation, competitive everywhere | Larger bundle than Solid/Lit |
| Angular | 1 | Fastest initial FCP/LCP | Slowest back navigation, largest bundle |
| Svelte | 0 | Second-smallest bundle, consistent performance | No category wins |
| Lit | 1 | Tied for detail navigation, small bundle | Slowest pagination |
SolidJS delivers the best overall performance, winning 6 of 8 categories. Its fine-grained reactivity system excels in scenarios involving component updates—filtering, pagination, and detail navigation—while also producing the smallest bundle.
React performs competitively across all metrics and dominates the "navigate back to list" scenario by a wide margin. Its mature ecosystem and consistent results make it an excellent choice for most applications.
Keep these caveats in mind when interpreting the results:
-
Not about Developer Experience (DX): This study focuses purely on runtime performance. It does not evaluate ease of development, debugging, learning curve, or ecosystem maturity.
-
SvelteKit only: The Svelte implementation uses SvelteKit. Results may differ with vanilla Svelte or alternative meta-frameworks.
-
No Server-Side Rendering (SSR): All frameworks were tested as client-side SPAs. SSR performance may differ significantly.
-
Latest versions only: Tests used the latest stable versions as of December 2025. Older versions may behave differently.
-
Single machine: All tests ran on one MacBook Pro. Results may vary on different hardware.
-
Synthetic data: The application uses generated mock data. Real-world apps with complex business logic may show different patterns.
-
High component count: This test emphasizes scenarios with many components. Applications with fewer, more complex components may yield different results.
-
AI-assisted development: Claude Code was used to help build each framework implementation, ensuring modern best practices and idiomatic patterns for each framework's latest version.
- Source Code: github.com/arobinson/FrontEndComparison
- Raw Data: Aggregated CSV | Full Results JSON

