Ziex
The full-stack web framework for Zig.
Compile-time safety, deterministic performance, and zero-dependency deployment. Build once, run everywhere.

Features
Everything you need to build fast, safe, and deployable web applications.
Fast Performance
Significantly faster at SSR than many other frameworks. Optimized for speed and low latency.
Familiar Syntax
Familiar JSX-like syntax, or plain HTML-style markup, with full access to Zig's control flow.
File System Routing
Just put files in folders to create routes. Simple and intuitive structure for any scale.
API Routes
Create API endpoints by adding route.zig files to your project. Native Zig performance.
Hybrid Rendering
Optional client-side rendering for interactive experiences when you need it. SSR by default.
Control Flow
if/else, for/while, and switch all work as expected. It's just Zig syntax.
Developer Tooling
CLI, hot reload, and editor extensions for the best development experience.
Deploy Anywhere
Standalone binaries, Cloudflare, Vercel (soon), or any WASI-compatible host. Build once, run anywhere.
Predictable Memory
Explicit memory management. No GC pauses, no hidden allocations. Total control over your app's footprint.
Performance
View benchmark →
Each framework runs in a Docker container limited to 2 CPU cores and 2 GB RAM.
Deploy Anywhere.
Ziex compiles to a single, statically linked binary or a WASI module, making it compatible with every major cloud provider and edge network.
Standalone
Deploy as a self-contained binary on Linux, macOS, or Windows. Statically linked with zero runtime dependencies.
Edge Anywhere
Deploy to the edge via WASI. Native support for Cloudflare and Vercel (soon).
Static
Generate a statically pre-rendered site at build time. Deploy to GitHub Pages, Netlify, S3, or any CDN.
Cross-Platform
Build for any target from any host. Easily cross-compile optimized binaries for x86_64, aarch64, and more.
Code Examples
Short, focused snippets that show how each feature feels in practice.
const ControlIfProps = struct { is_admin: bool };
pub fn ControlIf(
allocator: zx.Allocator,
props: ControlIfProps,
) zx.Component {
return (
<div @{allocator}>
{if (props.is_admin) (
<span>Admin</span>
) else (
<span>Member</span>
)}
</div>
);
}pub fn ControlIfOptional(allocator: zx.Allocator) zx.Component {
const maybe_name: ?[]const u8 = "Zig";
return (
<div @{allocator}>
{if (maybe_name) |name| (<span>{name}</span>)}
</div>
);
}pub fn ControlIfError(allocator: zx.Allocator) zx.Component {
const parsed =
std.fmt.parseInt(u32, "42", 10);
return (
<div @{allocator}>
{if (parsed) |value| (
<span>{value}</span>
) else |err| (
<span>{@errorName(err)}</span>
)}
</div>
);
}pub fn ControlWhile(allocator: zx.Allocator) zx.Component {
var i: usize = 0;
return (
<ul @{allocator}>
{while (i < 3) : (i += 1) (<li>Item {i + 1}</li>)}
</ul>
);
}pub fn ControlWhileOptional(allocator: zx.Allocator) zx.Component {
var maybe: ?u32 = 1;
return (
<ul @{allocator}>
{while (maybe) |value| : (maybe = if (value < 3) value + 1 else null) (
<li>{value}</li>
)}
</ul>
);
}pub fn ControlWhileError(allocator: zx.Allocator) zx.Component {
var first: bool = true;
return (
<div @{allocator}>
{while (parseOnce(&first)) |value| (
<span>{value}</span>
) else |err| (
<span>{@errorName(err)}</span>
)}
</div>
);
}
fn parseOnce(first: *bool) !u32 {
if (first.*) {
first.* = false;
return 42;
}
return error.Done;
}pub fn CacheComponent(allocator: zx.Allocator) zx.Component {
return (<Expensive @{allocator} @caching="10s" />);
}
fn Expensive(allocator: zx.Allocator) zx.Component {
// std.Thread.sleep(std.time.ns_per_s * 3);
return (<div @{allocator}></div>);
}pub fn Page(
ctx: zx.PageContext,
) zx.Component {
// std.Thread.sleep(std.time.ns_per_s * 3);
return (
<div @allocator={ctx.arena}>
<h1>Expensive Page</h1>
<p>I take too long to load.</p>
</div>
);
}
pub const options = zx.PageOptions{
.caching = .{ .seconds = 300 },
};pub fn Page(
ctx: zx.PageContext,
) zx.Component {
return (
<div @allocator={ctx.arena}>
<h1>Home</h1>
</div>
);
}pub fn Layout(
ctx: zx.LayoutContext,
children: zx.Component,
) zx.Component {
return (
<html @allocator={ctx.arena}>
<body>
{children}
</body>
</html>
);
}const ButtonProps = struct { label: []const u8 };
pub fn Button(
ctx: *zx.ComponentCtx(ButtonProps),
) zx.Component {
return (
<button @allocator={ctx.allocator}>
{ctx.props.label}
</button>
);
}pub fn Fragment(
allocator: zx.Allocator,
) zx.Component {
return (
<fragment @{allocator}>
<span>One</span>
<>
<p>Two</p><p>Three</p>
</>
</fragment>
);
}pub fn SpreadProps(
allocator: zx.Allocator,
) zx.Component {
const attrs =
.{ .class = "btn", .id = "cta" };
const class = "primary";
return (
<section @{allocator}>
<button {..attrs}>Spreading</button>
<button {class}>Shorthand</button>
</section>
);
}pub fn DynamicAttr(
allocator: zx.Allocator,
) zx.Component {
const is_active = true;
const class = if (is_active) "active" else "idle";
const color = if (is_active) "green" else "red";
return (
<section @{allocator}>
<button class={class}>Go</button>
<button style=`color: {color};`>
Template String
</button>
</section>
);
}pub fn Page(ctx: zx.PageContext) zx.Component {
const id = ctx.request.getParam("id");
return (
<section @allocator={ctx.arena}>
<h1>User {id}</h1>
</section>
);
}pub fn PUT(ctx: zx.RouteContext) !void {
try ctx.response.json(.{ .status = "ok" });
}
pub fn POST(ctx: zx.RouteContext) !void {
const Data = struct { id: u32 };
const data =
try ctx.request.json(Data, .{});
try ctx.response.json(.{
.id = data.?.id,
.created = true,
});
}pub fn GET(ctx: zx.RouteContext) !void {
try ctx.socket.upgrade({});
}
pub fn Socket(ctx: zx.SocketContext) !void {
try ctx.socket.write(
try ctx.fmt("{s}", .{ctx.message}),
);
}
pub fn SocketOpen(ctx: zx.SocketOpenContext) !void {
try ctx.socket.write("Opened!");
}var count = zx.signal(i32, 0);
pub fn Client(allocator: zx.Allocator) zx.Component {
return (<Counter @{allocator} @rendering={.client} />);
}
pub fn Counter(allocator: zx.Allocator) zx.Component {
return (
<div @{allocator} class="counter">
<button onclick={increment}>
Click Me: {&count}
</button>
</div>
);
}
fn increment(_: zx.EventContext) void {
count.set(count.get() + 1);
}