Skip to content
4 min readUpdated July 10, 2026

Ziex (Zig) vs Leptos (Rust) SSR Benchmark

A head-to-head SSR benchmark: Ziex (Zig) against Leptos (Rust), both in Docker on an equal 2-CPU/2GB budget rendering the same 50-row page. Ziex takes the full-router run, but the winner flips the moment you change how much framework each side turns on.

By Md. Zahin Afsar
#ziex#leptos#zig#Rust#ssr#benchmark#docker#performance

Ziex is a full-stack web framework for Zig: JSX-style markup right inside Zig code, file-based routing, one static binary. It pitches itself as fast enough to go head-to-head with the big Rust frameworks.

I love Rust, so a Zig framework going after Leptos was worth checking. One machine, two containers, same load generator, same page, same CPU and memory budget. And this time both sides run the real thing: a layout, a router, the full server-render path each framework ships. No stripped-down handler on either side.

Result: on my machine, under equal limits, Ziex won.

  • Throughput: Ziex ~92.8k req/s, Leptos ~70.6k req/s. Ziex ~31% ahead.
  • Median latency (p50): Ziex 0.43 ms, Leptos 0.71 ms. 1.67x faster.
  • Tail (p99): Ziex 2.08 ms, Leptos 2.37 ms.
  • Errors: zero on both, millions of requests each.

Code, Dockerfiles, and the harness: github.com/zahinafsar/ziex_zig-vs-leptos_rust

The results

Latency (lower better)
Throughput (higher better)
Left: latency, shorter is better. Right: throughput, taller is better. 20s run, 50 connections.
metricZiex (Zig)Leptos (Rust)
req/sec~92,850~70,624
avg latency0.54 ms0.71 ms
p500.43 ms0.71 ms
p901.06 ms1.16 ms
p992.08 ms2.37 ms
max6.73 ms8.52 ms
total reqs (20s)~1.86M~1.41M
success100%100%

20 seconds, 50 connections, each container capped at 2 CPUs and 2 GB.

The catch

Here is the part that makes this benchmark honest instead of a headline. Leptos's renderer is not slow. It is faster than Ziex's. The winner flips depending on how much of each framework you actually run.

Three measurements, same 50-row page:

  • Pure render, in a loop, no server: Leptos renders it in ~1,790 ns, Ziex in ~4,900 ns. Leptos ~2.7x faster.
  • Bare handler, no router (just to_html() behind one route): Leptos ~151k req/s, Ziex ~89k. Leptos wins.
  • Full routed SSR (both using their real router + layout, the run above): Ziex ~92.8k, Leptos ~70.6k. Ziex wins.

Same Leptos engine, three results. It goes from winning by a mile to losing, purely by turning on the framework around it.

So why does routing cost Leptos so much? Switching on leptos_router + leptos_actix is not "match a URL." It is the full isomorphic app server:

  • a reactive router context per request (signals for location, params, query),
  • streaming SSR (render_app_to_stream, chunked body, futures scheduling),
  • resource-serialization scripts appended to every response (__RESOLVED_RESOURCES=[], __PENDING_RESOURCES=[]) even with zero async resources.

Ziex's router is the opposite: file-based, resolved at build time into a static table, then a plain dispatch to the page and a lean render straight into a writer. No per-request reactive graph, no streaming envelope. Its throughput barely moves between configs (~87–93k everywhere) because it does the same small amount of work per request no matter what. Leptos swings from 151k to 70k because its framework does a lot more once you let it.

The static 50-row page rewards whichever framework does the least. That is Ziex here.

The workload

Both render the same page: a <main> with 50 rows, each <div>SSR {v}-{i}</div>, built by a server-side loop, fresh every request. No caching, no precomputed string. Same list Ziex uses in its own benchmark repo, so neither side got an easier page.

Ziex builds it with a template for loop:

zig
pub fn Page(ctx: zx.PageContext) zx.Component {
    const arr: [50]u32 = @splat(1);

    return (
        <main @allocator={ctx.arena}>
            {for (arr, 0..) |v, i| (
                <div>SSR {v}-{i}</div>
            )}
        </main>
    );
}

const zx = @import("zx");

Leptos builds the same rows in a component, wired through leptos_router and served by leptos_actix:

rust
#[component]
fn Page() -> impl IntoView {
    let items: Vec<u32> = (0..50).map(|_| 1).collect();

    view! {
        <main>
            {items
                .into_iter()
                .enumerate()
                .map(|(i, v)| view! { <div>"SSR " {v} "-" {i}</div> })
                .collect_view()}
        </main>
    }
}

#[component]
fn App() -> impl IntoView {
    view! {
        <Router>
            <Routes fallback=|| "Not found.">
                <Route path=path!("") view=Page/>
            </Routes>
        </Router>
    }
}

Both live behind docker compose, each pinned to 2 CPUs and 2 GB so neither can outspend the other:

yaml
deploy:
  resources:
    limits:
      cpus: "2"
      memory: 2g

Load comes from a third container running oha on the same bridge network, hitting each server by its internal name so neither pays for host port forwarding:

bash
oha -z 20s -c 50 --no-tui --output-format json http://ziex:3000/
oha -z 20s -c 50 --no-tui --output-format json http://leptos:3000/

Same box, same network, same generator, same budget, or it does not count.

So, should you use Ziex?

Nobody embarrassed themselves. Both did millions of requests, zero errors, sub-millisecond medians, on a page that actually renders data.

Ziex is genuinely fun: writing HTML inside Zig with real control flow, file-based routes, compiling to one static binary, is a lovely place to work. Its lean request path means the full framework costs almost nothing over a bare render, which is exactly why it took this benchmark. But it is early, on dev releases, with a WASM bundle it cannot yet turn off.

Leptos is the safer production bet today: mature stack, huge ecosystem, and a renderer that is genuinely faster once you stop paying for machinery you are not using. If your pages are static lists, that machinery is pure overhead and Ziex wins. If your pages are interactive and you want hydration and fine-grained reactivity, Leptos is doing real work for that cost, and you would want it.

Want to run it yourself? A compose file, two Dockerfiles, one bench.sh. Clone it and your numbers will differ from mine: github.com/zahinafsar/ziex_zig-vs-leptos_rust.