Performance

The same three pages — a static page, a server-rendered page with 100 items and one interactive counter, and a form — built in Devora.js and four other frameworks, each with its own documented production setup. Measured 2026-10-16; every number on this page comes from that run's JSON.

Benchmarks

In this run Devora.js was ahead of every other framework on server-rendered page, pre-rendered page and form post; level, within the run-to-run spread, on build time (with Astro, SvelteKit and React Router); behind on memory (SvelteKit) and JavaScript (SvelteKit).

Requests/second, server-rendered pageHigher is better

Requests/second, server-rendered page, higher is better
FrameworkResult
Devora.js 0.3.511,736 req/s
Astro 7.3.89,456 req/s
SvelteKit 3.0.17,122 req/s
React Router 8.4.03,944 req/s
Next.js 16.4.11,826 req/s

A page rendered per request: 100 items from a loader and one interactive counter. The scenario closest to a real application.

Requests/second, pre-rendered pageHigher is better

Requests/second, pre-rendered page, higher is better
FrameworkResult
Devora.js 0.3.5101,891 req/s
Astro 7.3.838,096 req/s
React Router 8.4.036,138 req/s
SvelteKit 3.0.136,036 req/s
Next.js 16.4.113,989 req/s

A tiny page with no data, built ahead of time. Each framework sends its own output (sizes under "Response size"), so this mostly measures the HTTP layer.

Requests/second, form POSTHigher is better

Requests/second, form POST, higher is better
FrameworkResult
Devora.js 0.3.579,136 req/s
SvelteKit 3.0.136,976 req/s
React Router 8.4.023,567 req/s
Astro 7.3.818,791 req/s
Next.js 16.4.18,585 req/s

A valid form submission answered with a 303 redirect.

Cold production buildLower is better

Cold production build, lower is better
FrameworkResult
React Router 8.4.0409 ms
Devora.js 0.3.5434 ms
Astro 7.3.8563 ms
SvelteKit 3.0.1971 ms
Next.js 16.4.12.17 s

npm run build with build output and caches deleted.

Idle server memoryLower is better

Idle server memory, lower is better
FrameworkResult
SvelteKit 3.0.1141 MB
React Router 8.4.0149 MB
Devora.js 0.3.5157 MB
Astro 7.3.8161 MB
Next.js 16.4.1175 MB

Resident memory 2 s after start.

JavaScript sent to the browser, SSR pageLower is better

JavaScript sent to the browser, SSR page, lower is better
FrameworkResult
SvelteKit 3.0.133.4 kB
Devora.js 0.3.545.6 kB
Astro 7.3.867.4 kB
React Router 8.4.0108.6 kB
Next.js 16.4.1132.0 kB

Every script the page loads, gzip.

Response size and encoding

What each framework actually sent for one request after the run, read as raw bytes. The load tool sends no Accept-Encoding, so nothing was compressed during the timed runs; the browser column adds what a browser sends.

FrameworkPre-rendered pageServer-rendered pageSame, with browser Accept-Encoding
Devora.js568 B4,902 B568 B / 4,902 B
Next.js5,559 B17,105 B1,811 B (gzip) / 3,563 B (gzip)
Astro398 B8,391 B398 B / 8,391 B
SvelteKit1,868 B7,824 B628 B (br) / 7,824 B
React Router4,601 B15,153 B1,754 B (br) / 3,463 B (br)

Apple M5, 10 cores, 16 GB, Darwin 27.0.0, Node 24.19.0. 50 connections, median of 5 runs (builds: 3), 2026-10-16. Client and server on one machine, tiny pages. Reproduce

Read this first

  • Devora.js runs on Node and Vite like most of the others. Requests/second on one machine mostly measures Node's HTTP server and the rendering work per request; it isn't how many users an app can serve.
  • Devora.js sends the same security headers (CSP, HSTS, X-Frame-Options) on every response by default; those are included in its numbers.
  • One machine, client and server together. Run it on your own hardware before relying on it.
  • The load tool sends no Accept-Encoding, so no framework compressed during the timed runs; the pages differ in size because each framework emits different HTML (see "Response size and encoding" in the panel). The pre-rendered page is tiny, and a smaller page is part of why a framework is fast on it.
  • On the pre-rendered page this run measured Devora.js 101,891, Astro 38,096, React Router 36,138, SvelteKit 36,036, Next.js 13,989 requests/s (medians of 5 runs). The page is tiny, so this mostly compares the HTTP layer, and a result is only called a lead when it is outside the other's run-to-run spread.

Where Devora.js is behind, level, or ahead

A difference smaller than the spread between a framework's own runs is called a tie.

Behind

  • Build, warm: 446 ms, 2nd of 5. React Router is ahead with 404 ms.
  • Server start: 194 ms, 3rd of 5. SvelteKit is ahead with 140 ms.
  • Idle memory: 157 MB, 3rd of 5. SvelteKit is ahead with 141 MB.
  • JS, SSR page (gzip): 45.6 kB, 2nd of 5. SvelteKit is ahead with 33.4 kB.

Level, within the run-to-run spread

  • Build, cold: 434 ms, level with Astro, SvelteKit and React Router.
  • JS, static page (gzip): 0.0 kB, level with Astro.
  • Counter responds after: 20 ms, level with Astro and SvelteKit.

Ahead of every other framework

Static page, req/s; SSR page, req/s; Form POST, req/s.

All results

Medians of the runs. Spreads (min–max of every run) are in the raw data.

MetricDevora.js 0.3.5Next.js 16.4.1Astro 7.3.8SvelteKit 3.0.1React Router 8.4.0
Build, cold
lower is better
434 ms2.17 s563 ms971 ms409 ms
Build, warm
lower is better
446 ms1.24 s517 ms978 ms404 ms
Server start
lower is better
194 ms242 ms165 ms140 ms170 ms
Idle memory
lower is better
157 MB175 MB161 MB141 MB149 MB
Static page, req/s
higher is better
101,891 (p99 1 ms)13,989 (p99 6 ms)38,096 (p99 1 ms)36,036 (p99 3 ms)36,138 (p99 2 ms)
SSR page, req/s
higher is better
11,736 (p99 8 ms)1,826 (p99 52 ms)9,456 (p99 9 ms)7,122 (p99 11 ms)3,944 (p99 20 ms)
Form POST, req/s
higher is better
79,136 (p99 1 ms)8,585 (p99 11 ms)18,791 (p99 6 ms)36,976 (p99 4 ms)23,567 (p99 4 ms)
JS, SSR page (gzip)
lower is better
45.6 kB132.0 kB67.4 kB33.4 kB108.6 kB
JS, static page (gzip)
lower is better
0.0 kB131.8 kB0.0 kB33.4 kB108.6 kB
Counter responds after
lower is better
20 ms34 ms23 ms22 ms35 ms

API routes

The same JSON API (identical payloads, status codes and validation, checked before timing) in Devora.js, Express, Fastify, Hono, NestJS on its Express and on its Fastify platform, and the API routes of Next.js, SvelteKit, React Router and Astro. Devora.js sends CSP, HSTS and X-Frame-Options on every response and runs a real session layer for the authenticated scenario; the bare backend frameworks do neither, so part of any gap to them is that work. Memory and start time for these apps are in the repository's backend report.

D. JSON list

GET /api/items: a JSON list of 100 items. Devora.js is behind Fastify 5 and others beyond the spread.

FrameworkRequests/s, median (min–max)p99
Fastify 5 5.12.766,678 (62,941–68,180)1 ms
Hono 4.13.1366,215 (65,210–66,644)1 ms
NestJS (Fastify platform) 12.1.361,927 (61,377–62,305)1 ms
Devora.js 0.3.549,529 (49,258–51,050)2 ms
Express 5 5.3.048,388 (46,477–48,499)1 ms
NestJS (Express platform) 12.1.344,700 (43,626–44,979)2 ms
SvelteKit 3.0.132,976 (30,394–33,430)2 ms
Astro 7.3.819,844 (19,746–19,854)5 ms
React Router 8.4.017,985 (17,109–18,087)4 ms
Next.js 16.4.112,069 (11,565–12,195)7 ms

E. Authenticated GET

GET /api/me with a Bearer token: a session lookup, then JSON. Devora.js is behind Fastify 5 and others beyond the spread.

FrameworkRequests/s, median (min–max)p99
Fastify 5 5.12.7134,383 (129,958–134,988)0 ms
Hono 4.13.13128,375 (123,418–129,440)0 ms
NestJS (Fastify platform) 12.1.3119,165 (118,816–119,212)0 ms
Express 5 5.3.085,420 (84,529–88,282)1 ms
NestJS (Express platform) 12.1.379,404 (79,159–80,480)1 ms
Devora.js 0.3.577,228 (76,431–77,635)2 ms
SvelteKit 3.0.150,291 (47,666–53,852)2 ms
Astro 7.3.822,319 (21,877–22,690)5 ms
React Router 8.4.021,080 (20,251–21,202)3 ms
Next.js 16.4.113,216 (12,418–13,656)7 ms

F1. POST, valid body

POST /api/items with a valid JSON body (201). Devora.js is behind Fastify 5 and others beyond the spread.

FrameworkRequests/s, median (min–max)p99
Fastify 5 5.12.7116,186 (113,568–116,617)0 ms
Hono 4.13.13106,772 (100,681–107,773)0 ms
NestJS (Fastify platform) 12.1.3100,425 (100,192–100,797)0 ms
Express 5 5.3.075,395 (73,104–75,645)1 ms
Devora.js 0.3.571,450 (69,767–74,167)2 ms
NestJS (Express platform) 12.1.365,789 (65,293–66,128)1 ms
SvelteKit 3.0.140,831 (38,706–42,029)3 ms
React Router 8.4.022,318 (22,033–23,106)5 ms
Astro 7.3.819,669 (19,282–20,072)5 ms
Next.js 16.4.111,911 (11,389–12,377)8 ms

F2. POST, invalid body

POST /api/items with an invalid body (422). Devora.js is behind Fastify 5 and others beyond the spread.

FrameworkRequests/s, median (min–max)p99
Fastify 5 5.12.7115,901 (111,124–116,337)0 ms
Hono 4.13.13109,402 (108,931–109,699)0 ms
NestJS (Fastify platform) 12.1.377,152 (76,132–78,048)1 ms
Devora.js 0.3.574,592 (70,423–75,302)1 ms
Express 5 5.3.073,545 (71,334–75,197)1 ms
NestJS (Express platform) 12.1.353,860 (53,750–54,009)1 ms
SvelteKit 3.0.142,003 (41,540–42,852)2 ms
React Router 8.4.023,307 (23,028–23,576)4 ms
Astro 7.3.820,059 (19,404–20,173)5 ms
Next.js 16.4.111,929 (11,783–12,422)8 ms

G. One async step

GET /api/slow: one awaited 2 ms timer, then JSON. Devora.js is level, within the run-to-run spread, with Fastify 5, Hono, NestJS (Fastify platform) and SvelteKit.

FrameworkRequests/s, median (min–max)p99
Devora.js 0.3.515,967 (15,348–17,436)5 ms
Fastify 5 5.12.715,455 (15,279–15,699)4 ms
Hono 4.13.1315,412 (15,332–15,512)4 ms
NestJS (Fastify platform) 12.1.315,226 (15,106–15,541)4 ms
SvelteKit 3.0.115,200 (15,056–15,947)5 ms
NestJS (Express platform) 12.1.315,132 (15,035–15,331)5 ms
Express 5 5.3.014,972 (14,519–15,240)5 ms
Astro 7.3.814,435 (14,230–14,685)6 ms
React Router 8.4.013,317 (13,032–13,383)6 ms
Next.js 16.4.113,293 (12,536–13,496)6 ms

How it was measured

Each framework's app renders the same content with the same status codes: /static pre-rendered at build time, /items rendered per request with a loader returning 100 items and one counter, /form answering a valid POST with a 303 and an empty one with the error. (Next.js answers an invalid Server Action submission with 200 and the error, its documented behavior.) Builds and servers are each framework's own: Devora.js devora build/devora start, Next.js next build/next start, Astro on @astrojs/node, SvelteKit on adapter-node, React Router with react-router-serve. Defaults only.

  • Load: autocannon 8.0.0, 50 connections, 5 s warm-up, then 5 runs of 10 s each, with a browser's Accept and Origin headers.
  • Builds: 3 cold builds (output and caches deleted), each followed by a warm one.
  • Browser: Chromium, 5 runs per page; JavaScript is every script the page loads, compressed with gzip -9 and brotli; "counter responds" is the time from navigation start until a click changes it.
  • Machine: Apple M5, 10 cores, 16 GB, Darwin 27.0.0 (darwin arm64), Node v24.19.0.
  • Versions: Devora.js 0.3.5, Next.js 16.4.1, Astro 7.3.8, SvelteKit 3.0.1, React Router 8.4.0.

Scripts, the five apps and the raw JSON: benchmarks/ in the devora.js repo. To reproduce: cd benchmarks && npm ci && npm run bench && npm run report.

Caveats

  • Localhost has no network latency; real deployments add it to every request.
  • The pages are small on purpose. Real apps spend most of their time in their own data access, which these numbers don't include.
  • Idle memory includes the npm start wrapper process, the same for every framework.
  • Hydration timing clicks every 5 ms on an unthrottled machine; a phone on a slow network would see larger differences in JavaScript size.
  • Results are from one run of the suite on one day; they will change as every framework changes.