# Ziex > Full-stack web framework for Zig. Compile-time safety, deterministic performance, absolute simplicity, and a delightful developer experience. ZX files use familiar HTML-style markup with full access to Zig control flow (`if`/`else`, `for`/`while`, `switch`). File-system routing, API routes, WebSockets, component/page caching, KV storage, and SQLite are built in. Deploy as a standalone binary, WASI edge module, or static site. Important notes: - ZX is stricter than HTML: all tags must be closed (including self-closing tags like `
`) - Components and pages receive an allocator (usually `ctx.arena` for request-scoped allocations) - File-system routing maps `pages/` and `routes/` to URLs; dynamic segments use `[param]` folders ## Docs - [Learn](https://ziex.dev/learn): Quick start covering core ZX concepts - [Reference](https://ziex.dev/reference): Language and API reference - [Examples](https://ziex.dev/examples): Interactive feature demos - [Playground](https://ziex.dev/playground): Try ZX in the browser - [GitHub](https://github.com/ziex-dev/ziex): Source repository ## Examples ### Control Flow #### if ```zx const ControlIfProps = struct { is_admin: bool }; pub fn ControlIf( allocator: zx.Allocator, props: ControlIfProps, ) zx.Component { return (
{if (props.is_admin) ( Admin ) else ( Member )}
); } ``` #### if optional capture ```zx pub fn ControlIfOptional(allocator: zx.Allocator) zx.Component { const maybe_name: ?[]const u8 = "Zig"; return (
{if (maybe_name) |name| ({name})}
); } ``` #### if error capture ```zx pub fn ControlIfError(allocator: zx.Allocator) zx.Component { const parsed = std.fmt.parseInt(u32, "42", 10); return (
{if (parsed) |value| ( {value} ) else |err| ( {@errorName(err)} )}
); } ``` #### switch ```zx const Role = enum { admin, member, guest }; const ControlSwitchProps = struct { role: Role }; pub fn ControlSwitch(allocator: zx.Allocator, props: ControlSwitchProps) zx.Component { return (
{switch (props.role) { .admin => (Admin), .member => (Member), else => (Guest), }}
); } ``` #### for ```zx const ControlForProps = struct { items: []const []const u8 }; pub fn ControlFor(allocator: zx.Allocator, props: ControlForProps) zx.Component { return ( ); } ``` #### while ```zx pub fn ControlWhile(allocator: zx.Allocator) zx.Component { var i: usize = 0; return ( ); } ``` #### while optional capture ```zx pub fn ControlWhileOptional(allocator: zx.Allocator) zx.Component { var maybe: ?u32 = 1; return ( ); } ``` #### while error capture ```zx pub fn ControlWhileError(allocator: zx.Allocator) zx.Component { var n: u32 = 1; return (
{while (parseOnce(&n)) |value| ( {value}
) else |err| ( {@errorName(err)} )}
); } fn parseOnce(n: *u32) !u32 { if (n.* < 100) { n.* += 1; return n.*; } return error.Done; } ``` ### Caching #### component ```zx pub fn CacheComponent( allocator: zx.Allocator, ) !zx.Component { return (); } fn Slow(allocator: zx.Allocator) !zx.Component { try std.Io .sleep(zx.io(), .fromSeconds(2), .awake); return (
Slow
); } ``` #### page ```zx pub fn Caching__Page( ctx: zx.PageContext, ) !zx.Component { try std.Io .sleep(zx.io(), .fromSeconds(2), .awake); return (

Expensive Page

I take too long to load.

); } pub const CachingPage__options = zx.PageOptions{ .caching = .{ .ttl = .fromSeconds(300) }, }; ``` ### File System Routing #### page ```zx pub fn Routing__Page( ctx: zx.PageContext, ) zx.Component { return (

Home

); } ``` #### layout ```zx pub fn Layout( ctx: zx.LayoutContext, children: zx.Component, ) zx.Component { return ( {children} ); } ``` ### Component #### basics ```zx const ButtonProps = struct { label: []const u8 }; pub fn Button( ctx: *zx.ComponentCtx(ButtonProps), ) zx.Component { return ( ); } ``` #### fragment ```zx pub fn Fragment( allocator: zx.Allocator, ) zx.Component { return ( One <>

Two

Three

); } ``` #### props ```zx pub fn SpreadProps( allocator: zx.Allocator, ) zx.Component { const attrs = .{ .class = "btn", .id = "cta" }; const class = "primary"; return (
); } ``` #### attr ```zx 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 (
); } ``` ### Dynamic Path ```zx pub fn Page(ctx: zx.PageContext) zx.Component { const id = ctx.request.params.get("id"); return (

User {id}

); } ``` ### Expressions ```zx pub fn Expressions(allocator: zx.Allocator) zx.Component { const name = "Alice"; const count: u32 = 42; const price: f32 = 19.99; const active = true; return (

{name}

{count}

${price}

{active}

); } ``` ### Builtin Attributes #### allocator ```zx pub fn AllocatorDemo(ctx: zx.PageContext) zx.Component { const message = "Allocator is inherited by children"; return (

{message}

Use ctx.arena for request-scoped allocations.

); } ``` #### escaping ```zx pub fn RawHtml(allocator: zx.Allocator) zx.Component { const trusted = "Trusted HTML"; return (
{trusted}
{trusted}
); } ``` ### Importing ```zx pub fn ImportDemo(allocator: zx.Allocator) zx.Component { return (
); } ``` ### Component #### children ```zx const CardChildrenProps = struct { title: []const u8, children: zx.Component }; pub fn CardDemo(allocator: zx.Allocator) zx.Component { return (

This content is passed as children.

); } fn Card(allocator: zx.Allocator, props: CardChildrenProps) zx.Component { return (

{props.title}

{props.children}
); } ``` ### API Route ```zx 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 = if (data) |d| d.id else 0, .created = true, }, .{}); } ``` ### WebSocket ```zx 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!"); } ``` ### Client-side Rendering ```zx pub fn Client(allocator: zx.Allocator) zx.Component { return (); } pub fn Counter(ctx: *zx.ComponentCtx(void)) zx.Component { const count = ctx.state(i32, 0); return (
); } fn increment(e: *zx.client.Event.Stateful) void { const count = e.state(i32); count.set(count.get() + 1); } ``` ### Plugin #### tailwind ```zx pub fn buildTailwind(b: *std.Build) !void { const exe = b.addExecutable(.{ .name = "my-app" }); var zx_build = try ziex.init(b, exe, .{}); var assetsdir = zx_build.assetsdir; zx_build.plugin( zx.plugins.tailwind(b, .{ .input = b.path("app/assets/style.css"), .output = assetsdir.path(b, "style.css"), }), ); } ``` #### esbuild ```zx pub fn buildEsbuild(b: *std.Build) !void { const exe = b.addExecutable(.{ .name = "my-app" }); var zx_build = try ziex.init(b, exe, .{}); var assetsdir = zx_build.assetsdir; zx_build.plugin( zx.plugins.esbuild(b, .{ .input = b.path("app/main.ts"), .output = assetsdir.path(b, "main.js"), }), ); } ``` ### Learn #### greeting ```zx pub fn Learn__HelloWorld(allocator: zx.Allocator) zx.Component { return (

Welcome to my app

); } fn Learn__Greeting(allocator: zx.Allocator) zx.Component { return (

Hello, World!

); } ``` #### markup ```zx pub fn Learn__AboutSection(allocator: zx.Allocator) zx.Component { return (

About

Hello there.
How are you?

); } fn Learn__AboutCard(allocator: zx.Allocator) zx.Component { return (

User Profile

Welcome to the card component!

); } ``` #### text expression ```zx pub fn Learn__UserGreeting(allocator: zx.Allocator) zx.Component { const user_name = "Alice"; const greeting = "Welcome back"; return (

{greeting}, {user_name}!

Your profile is ready.

); } ``` #### format expression ```zx pub fn Learn__ProductInfo(allocator: zx.Allocator) zx.Component { const price: f32 = 19.99; const quantity: u32 = 3; const is_available = true; return (

Price: ${price}

Quantity: {quantity}

Available: {is_available}

); } ``` #### conditional ```zx pub fn Learn__UserStatus(allocator: zx.Allocator) zx.Component { const is_logged_in = true; const is_admin = false; return (
{if (is_logged_in) (

Welcome back!

) else (

Please log in.

)} {if (is_admin) ()}
); } ``` #### switch ```zx const Learn__Role = enum { admin, member, guest }; pub fn Learn__RoleBadge(allocator: zx.Allocator) zx.Component { const role: Learn__Role = .admin; return (
{switch (role) { .admin => (Admin), .member => ("Member"), .guest => ("Guest"), }}
); } ``` #### list ```zx pub fn Learn__ProductList(allocator: zx.Allocator) zx.Component { const products = [_][]const u8{ "Apple", "Banana", "Orange" }; return (

Products

    {for (products) |product| (
  • {product}
  • )}
); } ``` #### props ```zx const Learn__ButtonProps = struct { title: []const u8 = "Click Me", class: []const u8 = "btn", }; pub fn Learn__ButtonDemo(allocator: zx.Allocator) zx.Component { return (
); } fn Learn__Button(allocator: zx.Allocator, props: Learn__ButtonProps) zx.Component { return (); } ``` #### page ```zx pub fn Learn__Page(ctx: zx.PageContext) zx.Component { return (

About Us

Welcome to our website!

Path: {ctx.request.pathname}

); } ``` #### layout ```zx pub fn Learn__Layout(ctx: zx.LayoutContext, children: zx.Component) zx.Component { return ( My App
{children}
© My App
); } ``` #### build manual ```zx pub fn Learn__build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); const app_exe = b.addExecutable(.{ .name = "ziex_app", .root_module = b.createModule(.{ .root_source_file = b.path("app/main.zig"), .target = target, .optimize = optimize, }), }); _ = try ziex.init(b, app_exe, .{}); } ``` #### dynamic route ```zx pub fn Learn__UserProfile(ctx: zx.PageContext) zx.Component { const user_id = ctx.request.params.get("id") orelse "unknown"; return (

User Profile

User ID: {user_id}

); } ``` #### fragment ```zx pub fn Learn__FragmentDemo(allocator: zx.Allocator) zx.Component { return (
<>

Welcome

Multiple elements without a wrapper

); } ``` #### children ```zx const Learn__CardProps = struct { title: []const u8, children: zx.Component }; pub fn Learn__CardDemo(allocator: zx.Allocator) zx.Component { return (

This content is passed as children.

); } fn Learn__Card(allocator: zx.Allocator, props: Learn__CardProps) zx.Component { return (

{props.title}

{props.children}
); } ``` #### dynamic attributes ```zx pub fn Learn__DynamicAttrs(allocator: zx.Allocator) zx.Component { const class_name = "primary-btn"; const user_id = "user-123"; const is_active = true; return (
Dynamic class
); } ``` #### onclick ```zx pub fn Learn__Interactivity(allocator: zx.Allocator) zx.Component { return (); } pub fn Learn__EventHandling(allocator: zx.Allocator) zx.Component { return (
); } fn Learn__handleClick(_: zx.client.Event) void { zx.log.info("Clicked!", .{}); } fn Learn__handleInput(event: zx.client.Event) void { const v = event.value() orelse ""; zx.log.info("Input: {s}", .{v}); } ``` ### Reference Inline #### attrs ```zx pub fn unwrap__template_attrs() void { const count = 42; const name = "John"; // ... _ = ( ); // Renders: //
// Profile //
} ``` #### expr string ```zx pub fn unwrap__ref_expr_string() void { const name = "Alice"; // ... _ = (

{name}

); //

Alice

} ``` #### expr int ```zx pub fn unwrap__ref_expr_int() void { const count: u32 = 42; // ... _ = (

{count}

); //

42

} ``` #### expr float ```zx pub fn unwrap__ref_expr_float() void { const price: f32 = 19.99; // ... _ = (

${price}

); //

$19.99

} ``` #### expr bool ```zx pub fn unwrap__ref_expr_bool() void { const active = true; // ... _ = (

{active}

); //

true

} ``` #### expr enum ```zx pub fn unwrap__ref_expr_enum() void { const Status = enum { pending, approved, rejected }; const status = Status.approved; // ... _ = (

{status}

); //

approved

} ``` #### expr optional ```zx pub fn unwrap__ref_expr_optional() void { const maybe_name: ?[]const u8 = "Bob"; // ... _ = (

{maybe_name}

); //

Bob

const no_name: ?[]const u8 = null; // ... _ = (

{no_name}

); //

(empty) } ``` #### expr component ```zx pub fn unwrap__ref_expr_component() void { const header = zx.Component{ .text = "Header" }; // ... _ = (
{header}
); // Renders the Header component } ``` #### expr component array ```zx pub fn unwrap__ref_expr_component_array() void { const items: []const zx.Component = &.{ zx.Component{ .text = "A" }, zx.Component{ .text = "B" } }; // ... _ = (
    {items}
); // Renders both Item components } ``` #### optional capture ```zx pub fn unwrap__ref_optional_capture() void { const user_name: ?[]const u8 = "Alice"; // ... _ = if (user_name) |name| (

Welcome, {name}!

); // With else branch: _ = if (user_name) |name| (

Hello, {name}

) else (

Guest

); // While with capture (iterator pattern): var maybe_item: ?[]const u8 = "item"; _ = while (maybe_item) |item| : (maybe_item = null) (
  • {item}
  • ); } ``` #### error capture ```zx pub fn unwrap__ref_error_capture() void { const parsed = std.fmt.parseInt(u32, "42", 10); // If with error capture: _ = if (parsed) |value| (

    Value: {value}

    ) else |err| (

    Error: {@errorName(err)}

    ); // While with error capture: var first = true; _ = while (parseOnceRefInline(&first)) |value| (
  • {value}
  • ) else |err| (

    Iteration ended: {@errorName(err)}

    ); } fn parseOnceRefInline(first: *bool) !u32 { if (first.*) { first.* = false; return 1; } return error.Done; } ``` #### spread attrs ```zx pub fn unwrap__ref_spread_attrs() void { const form_attrs = .{ .class = "form-control", .@"data-validate" = "true", }; const input_props = .{ .name = "email", .value = "test@example.com" }; // ... _ = (
    ); } ``` #### shorthand attrs ```zx pub fn unwrap__ref_shorthand_attrs() void { const class = "btn-primary"; const disabled = true; const @"data-id" = "123"; _ = (); // Renders: } ``` #### template attrs ```zx pub fn unwrap__ref_template_attrs() void { const count = 42; const name = "John"; // ... _ = ( ); // Renders: //
    // Profile //
    } ``` #### component context ```zx pub fn RefInlineNoProps__Container(ctx: *zx.ComponentContext) zx.Component { return (
    {ctx.children}
    ); } ``` #### component ctx props ```zx pub const RefInline__CardProps = struct { title: []const u8 }; pub fn RefInline__Card(ctx: *zx.ComponentCtx(RefInline__CardProps)) zx.Component { return (

    {ctx.props.title}

    {ctx.children}
    ); } ``` #### multiline strings ```zx pub fn unwrap__ref_multiline_strings() void { // Multiline strings use Zig's \\ syntax: _ = (
                {
                \\const x = 1;
                \\const y = 2;
                \\const sum = x + y;
            }
            
    ); } ``` #### comments ```zx pub fn unwrap__ref_comments() void { // Single-line comments are supported in ZX: _ = (
    // This is a comment

    Visible content

    // Another comment
    ); } ``` #### route ctx ```zx pub fn RefInline__GET(ctx: zx.RouteContext) !void { _ = ctx.request; // Request details _ = ctx.response; // Set headers, body, json, etc. _ = ctx.socket; // WebSocket upgrade and pub/sub _ = ctx.arena; // Request-scoped allocator } ``` #### socket ctx ```zx pub fn RefInline__Socket(ctx: zx.SocketContext) !void { _ = ctx.socket; // WebSocket interface _ = ctx.message; // Received message data _ = ctx.data; // Custom data from upgrade _ = ctx.arena; // Message-scoped allocator } ``` #### component caching basic ```zx pub fn unwrap__ref_component_caching_basic() void { // Cache for 10 seconds _ = (
    ); // Cache for 5 minutes _ = (