merjs - Zig-native web framework

4 min read Original article ↗

merjs v0.2.5

A Zig-native web framework that ships without Node.

File-based routing, SSR, type-safe APIs, hot reload, Cloudflare Workers deploys, and client-side WASM. Same developer shape, but much faster and much leaner: 115,093 req/s locally and just 4.8 MB RAM under CI load.

< 5 mscold start

115kreq/s

4.8 MBRAM

260 KBbinary

At a glance

Why merjs feels materially different

The workflow feels familiar. The runtime profile does not. merjs keeps file routing, layouts, streaming, and typed APIs, then drops the Node-first assumptions beneath them.

native runtimeNo Node process and no package-manager boot tax.

edge-readySame app model can target Cloudflare Workers as a single WASM bundle.

bench board

The numbers are the story: faster startup, higher throughput, lower latency, lower RAM, smaller binary.

Cold startApple M-series local runs

Homepage throughputwrk -t4 -c50

Average latencylower is better

RAM under loadGitHub Actions benchmark

Binary footprintrelease build

Framework surface

What merjs gives you out of the box

routing

Pages, APIs, layouts, and 404s live in the filesystem.

app/ handles SSR pages. api/ returns typed JSON. Layout and 404 handling stay in the same primitive layer instead of being bolted on later.

rendering

Classic SSR, shell-first flushing, and true streaming routes.

merjs can render a full page, stream chunks, or split layout shell from body without dragging React in just to unlock the pattern.

typed api

Validation and JSON stay in Zig.

Zig structs, comptime validation, and std.json remove the extra schema layer and the usual drift between code and transport.

client

Zig to the browser via wasm32-freestanding.

Compile client logic directly to WASM, keep one language end-to-end, and avoid a second JS build chain just to make the browser interactive.

hot reload

Save a file, the browser reloads.

The in-process file watcher pushes changes over /_mer/events SSE. No JS bundler in the loop, no devtools agent.

prerender

Static HTML for any route that opts in.

Mark a page prerender = true and zig build prod writes static HTML to dist/. Same routes, no server.

dynamic routes

File-based params, no router config.

app/users/[id].zig becomes /users/:id. Path params land on the request struct, typed and ready.

sessions

Cookie sessions baked into the framework.

Signed and encrypted by default, configurable per-app, and zero external services to stand up. No Redis, no JWT library, no sidecar.

static assets

Anything in public/ ships at the root.

Hits disk once on first request, then served from in-memory cache. MIME types and ETags handled by the framework.

mercss

Built-in utility CSS engine.

Scans templates at codegen time and emits only the classes you actually used. Supports hover:, md:, dark:, and arbitrary w-[42px] values — no Tailwind, no PostCSS.

observability

Latency, status, and errors wired through the request lifecycle.

Telemetry lives in the framework, not a sidecar. Drop in your sink (stdout, OTLP, Sentry) without redoing the request path.

deploy anywhere

Cloudflare, Vercel, Render, Fly, or your own box.

The same Zig source compiles to a native binary or a wasm32 edge bundle. Ship to Cloudflare Workers, Vercel Functions, Render web services, Fly machines, Railway, or any Linux host — no rewrites, no provider-specific shape.

Philosophy

What merjs is actually betting on

01

The stack should compile down, not boot up.

The server is a native binary. The client is WASM. The runtime is the platform you are already deploying to.

02

Type safety should be native to the language.

Validation, request parsing, and serialization stay in Zig instead of spreading across schema files and runtime adapters.

03

Developer experience should not require a heavyweight runtime tax.

Keep the good parts: routing, layouts, reloads, edge deploys. Remove the layers that only existed because JavaScript had to be everywhere.

Current release

v0.2.5 ships the Zig 0.16 migration, edge installer, and one-line install.

The release page goes deeper into memory reuse, caching, shell-first paint, fetch strategy, and the concrete 4.8 MB vs 71.7 MB RAM story.