SnapFire FSR Arena
The same storefront written in SnapFire FSR, Next.js, SvelteKit, Nuxt, Leptos and Dioxus, built for production and served on one machine: build time, deploy size, bytes per page and requests per second.
Results
Test run
- Machine
- Apple M4 Pro (14 cores)
- Measured
- 2026-11-03T16:53:25Z
| Implementation | Crate | Version |
|---|---|---|
| dioxus | Dioxus | dioxus 0.7.10 |
| fsr | SnapFire FSR | snapfire_fsr_host 0.25.1 |
| leptos | Leptos | leptos 0.8.23 |
| nextjs | Next.js | next 16.4.4, react 19.3.1 |
| nuxt | Nuxt | nuxt 4.6.4, vue 3.5.44 |
| sveltekit | SvelteKit | @sveltejs/kit 3.1.1, svelte 5.57.3 |
Method
Every application is the same storefront: 48 products in a module each imports, a landing page and a counter page built once at build time and a catalog, product and search page rendered for each request because each reads the query or a parameter. The data, the compute code and the stylesheet are shared byte for byte, with the data and compute code written once in TypeScript and once in Rust for Leptos and Dioxus. The views are shared as TSX by FSR and Next.js and written once each as Svelte, Vue, Leptos and Dioxus components for those four, with the same markup.
Three compute pages run a loader that loops and allocates per request, the work a JIT is built for: a sieve of Eratosthenes, an aggregation of generated orders sorted with a comparator and a word count over a built-up text. A table page renders 500 rows from simple data. Each writes a digest of what it computed and a run refuses to start when the applications' digests differ. FSR is measured on the three compute pages a second time with the loader calling the Rust that Leptos and Dioxus run through #[native]. That figure is shown under its own.
FSR is built with fsr build app and then cargo build --release under RELEASE_ENV=production, which compiles each loader the generator can write into the binary, then prerenders its fixed routes. Its render memo ([cache]) is off, so every live request renders. Next.js is built with next build and served from its standalone output with node server.js; the live pages set dynamic = "force-dynamic". SvelteKit is built with vite build and @sveltejs/adapter-node and served with node build; each live page computes in a +page.server.ts load. Nuxt is built with nuxt build and served with node .output/server/index.mjs; each live page computes in useAsyncData. Both prerender the landing and counter pages. Leptos is built with cargo leptos build --release and served by its Axum binary; each live page computes in a server function read through a blocking resource. Every Leptos page renders per request, the landing and counter pages included. Dioxus is built with dx bundle --platform web --fullstack --release and served by the server binary it writes; each live page computes in a server function read through use_server_future. Every Dioxus page also renders per request.
A cold build starts from empty output directories with dependencies already downloaded and node_modules installed: a fresh CARGO_TARGET_DIR and no rustc wrapper for FSR, Leptos and Dioxus, a fresh distDir for Next.js and fresh output, build and Vite cache directories for SvelteKit and Nuxt. A warm build touches one page and builds again.
The deploy tree is what a server needs: for FSR the tree fsr bundle writes plus config/ and the binary as built with debug information; for Next.js the standalone directory with its static files copied in; for SvelteKit the adapter's build/ and package.json; for Nuxt .output/; for Leptos its site/ directory and the binary; for Dioxus the web/ directory dx writes, which holds the server binary and public/. Next.js, SvelteKit and Nuxt also need Node installed, listed apart.
Bytes per page are what a browser fetches to show it: the document, its stylesheets, the WebAssembly it preloads and its scripts, following the static imports of module scripts. Modules reached only by a dynamic import() are listed apart as loaded later. Sizes are raw and gzipped at the default level.
Each load cell holds one HTTP/1.1 connection per concurrent client and sends the next request when the last body is read, for a fixed number of seconds. Each repetition starts every server afresh and the applications alternate, their order flipping between repetitions. Every page is checked for its status, its text and its product count before it is loaded. Figures are medians across repetitions.
Reproducibility
The load client runs on the same machine as the server and takes CPU from it.
FSR, Leptos and Dioxus serve on every core. Next.js, SvelteKit and Nuxt each serve from one Node process; running several behind a balancer is a deployment choice this arena does not make.
