Benchmarks
zerolith 1.5.0 next to hand-written JavaScript and seven popular frameworks on js-framework-benchmark, measured again with every release.
Scores
zerolith 1.5.0 scores 1.12, the second-fastest of eight frameworks.
Lower is faster. Hand-written JavaScript is 1.00, and each bar is the extra time a framework takes over it across the nine operations.
| Framework | Score | Extra time over hand-written JavaScript |
|---|---|---|
| vanillavanillajs hand-written | 1.00 | |
| Solidsolid 1.9.3 | 1.10 | |
| zerolithzerolith 1.5.0 | 1.12 | |
| Sveltesvelte 5.42.1 | 1.15 | |
| Litlit 3.2.0 | 1.22 | |
| Vuevue 3.5.39 | 1.27 | |
| Angularangular-cf-signals-nozone 22.2.2 | 1.35 | |
| Preactpreact-signals 11.0.0 + 2.3.1 | 1.36 | |
| Reactreact-hooks 19.2.0 | 1.48 | |
| 0%+10%+20%+30%+40% | ||
Every operation
Each cell is the median time in milliseconds, over its factor against hand-written JavaScript at that operation; below 1.00 is faster than it. The operations marked with a slowdown run with Chrome slowing the CPU by that factor.
| Operation | vanilla | Solid | zerolith | Svelte | Lit | Vue | Angular | Preact | React |
|---|---|---|---|---|---|---|---|---|---|
| create 1,000 rows | 69.81.00 | 76.91.10 | 82.51.18 | 78.11.12 | 84.81.21 | 85.31.22 | 104.91.50 | 108.81.56 | 92.61.33 |
| replace all 1,000 rows | 76.51.00 | 86.81.13 | 91.01.19 | 94.81.24 | 91.21.19 | 100.11.31 | 109.71.43 | 116.71.53 | 107.51.41 |
| update every 10th row (4x slowdown) | 46.71.00 | 48.91.05 | 51.61.10 | 50.41.08 | 53.81.15 | 57.71.24 | 50.41.08 | 52.41.12 | 63.11.35 |
| select a row (4x slowdown) | 8.31.00 | 11.01.33 | 9.31.12 | 15.61.88 | 17.22.07 | 11.61.40 | 17.32.08 | 12.61.52 | 18.02.17 |
| swap 2 rows (4x slowdown) | 56.71.00 | 62.11.10 | 63.31.12 | 66.31.17 | 65.81.16 | 65.11.15 | 47.60.84 | 62.21.10 | 398.37.02 |
| remove one row (2x slowdown) | 47.71.00 | 49.51.04 | 49.11.03 | 48.91.03 | 51.91.09 | 59.31.24 | 54.31.14 | 50.31.05 | 52.71.10 |
| create 10,000 rows | 794.71.00 | 865.91.09 | 901.31.13 | 895.31.13 | 932.81.17 | 970.31.22 | 1077.71.36 | 1122.31.41 | 1126.31.42 |
| append 1,000 rows to 1,000 | 88.31.00 | 89.81.02 | 93.11.05 | 95.31.08 | 97.71.11 | 103.31.17 | 117.41.33 | 117.41.33 | 113.11.28 |
| clear 1,000 rows (4x slowdown) | 33.81.00 | 41.81.24 | 37.81.12 | 41.81.24 | 51.11.51 | 55.21.63 | 56.61.67 | 57.51.70 | 66.81.98 |
Memory, size, and first paint
Each value is the median of 5 passes, since the benchmark takes one sample of each a pass. The size rows measure each benchmark app's bundle, the framework code it uses and its own, not a framework's whole package.
| Measure | vanilla | Solid | zerolith | Svelte | Lit | Vue | Angular | Preact | React |
|---|---|---|---|---|---|---|---|---|---|
| ready memory, after load (MB) | 0.56 | 0.58 | 0.73 | 0.61 | 0.70 | 0.86 | 1.11 | 0.69 | 1.18 |
| run memory, with 1,000 rows (MB) | 1.87 | 2.68 | 2.82 | 2.87 | 2.79 | 3.91 | 3.61 | 4.90 | 4.42 |
| after creating and clearing 1,000 rows 5 times (MB) | 0.64 | 0.77 | 0.97 | 0.98 | 0.88 | 1.19 | 1.69 | 8.95 | 1.98 |
| size uncompressed (kB) | 11.3 | 11.5 | 29.9 | 26.6 | 22.1 | 64.3 | 110.6 | 23.3 | 190.3 |
| size compressed (kB) | 2.5 | 4.5 | 9.0 | 9.7 | 7.3 | 23.3 | 33.8 | 8.3 | 51.4 |
| first paint (ms) | 118.4 | 121.9 | 159.2 | 139.1 | 147.0 | 216.8 | 286.2 | 139.2 | 452.0 |
Every run
Each run's scores, the newest release first, with how the run was taken; lower is faster. A score compares frameworks within its own run, so a change in how runs are taken moves every framework's score with it.
| Run | vanilla | Solid | zerolith | Svelte | Lit | Vue | Angular | Preact | React |
|---|---|---|---|---|---|---|---|---|---|
| 1.5.02026-11-08medians of 15 runs on a GitHub-hosted runner | 1.00 | 1.10 | 1.12 | 1.15 | 1.22 | 1.27 | 1.35 | 1.36 | 1.48 |
| 1.3.02026-11-07medians of 15 runs on a GitHub-hosted runner | 1.00 | 1.12 | 1.15 | 1.17 | 1.27 | 1.29 | 1.43 | 1.38 | 1.51 |
| 1.2.02026-11-07medians of 15 runs on a GitHub-hosted runner | 1.00 | 1.09 | 1.10 | 1.13 | 1.23 | 1.27 | 1.41 | 1.35 | 1.49 |
| 1.1.02026-11-06medians of 15 runs on a GitHub-hosted runner | 1.00 | 1.10 | 1.10 | 1.14 | 1.24 | 1.27 | 1.45 | 1.35 | 1.49 |
| 1.0.12026-11-06medians of 15 runs on a GitHub-hosted runner | 1.00 | 1.11 | 1.14 | 1.16 | 1.27 | 1.28 | 1.42 | 1.37 | 1.51 |
| 1.0.02026-11-05medians of 15 runs on a GitHub-hosted runner | 1.00 | 1.13 | 1.15 | 1.17 | 1.27 | 1.26 | 1.43 | 1.37 | 1.46 |
| 0.1.0-alpha.102026-11-04medians of 15 runs on a GitHub-hosted runner | 1.00 | 1.13 | 1.13 | 1.16 | 1.27 | 1.26 | 1.41 | 1.40 | 1.53 |
| 0.1.0-alpha.92026-11-03medians of 15 runs on a GitHub-hosted runner | 1.00 | 1.10 | 1.13 | 1.14 | 1.24 | 1.30 | 1.41 | 1.36 | 1.46 |
| 0.1.0-alpha.82026-11-03medians of 15 runs on a GitHub-hosted runner | 1.00 | 1.14 | 1.13 | 1.18 | 1.27 | 1.29 | 1.43 | 1.40 | 1.50 |
| 0.1.0-alpha.82026-11-02medians of 15 runs, held to one group of cores on an idle machine | 1.00 | 1.07 | 1.08 | 1.10 | 1.18 | 1.19 | 1.29 | 1.27 | 1.40 |
| 0.1.0-alpha.62026-11-01means of 10 runs, the cores left to the system | 1.00 | 1.13 | 1.13 | 1.14 | 1.23 | 1.23 | 1.37 | 1.33 | 1.47 |
| 0.1.0-alpha.52026-10-31means of 10 runs, the cores left to the system | 1.00 | 1.11 | 1.12 | 1.14 | 1.23 | 1.26 | 1.42 | 1.34 | 1.44 |
0.1.0-alpha.6, 2026-11-01: 0.1.0-alpha.7 builds the same bundle byte for byte, so this run stands for it too.
How the runs are measured
js-framework-benchmark renders the same table of rows in every framework and times nine operations on it, from creating 1,000 rows to clearing them. Its driver clicks a button in Chrome and measures from the click to the end of the paint it causes, from Chrome's performance trace, so script, style, layout, and paint all count, and five of the operations run with the CPU slowed down, as the official runs do. Each time here is the median of a run's samples: 15 of each operation, and 25 of selecting a row.
Every framework is the keyed entry of the benchmark's repository at the commit frameworks.json names, built from its own lockfile, and zerolith is the entry in this repository, pinned to the release. The memory results are what Chrome reports for the page after an action and a forced garbage collection, and the size results are the bytes of the entry's files; the benchmark takes one sample of each a pass, so each release's run repeats them for five passes and keeps the medians.
Each release is measured once it is published, by the benchmarks workflow, and the run is kept in the repository with the machine and method it used. A change of machine or method moves every framework's score, so scores compare only within one run. The earlier runs are listed under Every run, each with how it was taken.
These are this project's runs, not the official results, which the benchmark's maintainer measures and publishes at krausest.github.io/js-framework-benchmark; zerolith is not among them until its entry is accepted upstream. The benchmarks README has the full method and the steps to take a run yourself.