Examples

Explore the examples showcasing use of ZX in different scenarios. Each example includes the codes and it's preview.

If/Else Statements

Use if/else expressions to conditionally render content based on state. ZX supports inline conditional expressions that return components or text.

pub fn Page(allocator: zx.Allocator) zx.Component {
    const is_logged_in = true;
    const is_premium = false;

    return (
        <main @allocator={allocator}>
            {if (is_logged_in) (
                <div>
                    <h2>Welcome back!</h2>
                    {if (is_premium) (
                        <p>Enjoy your premium features.</p>
                    ) else (
                        <p>Upgrade to premium for more features.</p>
                    )}
                </div>
            ) else (
                <div>
                    <h2>Hello, Guest</h2>
                    <p>Please log in to continue.</p>
                </div>
            )}
        </main>
    );
}

const zx = @import("zx");
<main><div><h2>Welcome back!</h2><p>Upgrade to premium for more features.</p></div></main>
Tip: You can nest if/else expressions for more complex conditional logic.

Switch Expressions

Switch expressions provide pattern matching for enums and other types. Each branch can return a component or text.

pub fn Page(allocator: zx.Allocator) zx.Component {
    const status: Status = .active;

    return (
        <main @allocator={allocator}>
            <h2>Account Status</h2>
            <div class="status">
                {switch (status) {
                    .active => (<span style="color: green">● Active</span>),
                    .pending => (<span style="color: orange">● Pending</span>),
                    .inactive => (<span style="color: red">● Inactive</span>),
                }}
            </div>
            <p>
                {switch (status) {
                    .active => ("Your account is in good standing."),
                    .pending => ("Your account is awaiting approval."),
                    .inactive => ("Your account has been deactivated."),
                }}
            </p>
        </main>
    );
}

const zx = @import("zx");

const Status = enum { active, pending, inactive };
<main><h2>Account Status</h2><div class="status"><span style="color: green">● Active</span></div><p>Your account is in good standing.</p></main>

For Loop

Iterate over arrays and slices to render lists of components. Each iteration receives the current item which you can use in your template.

pub fn Page(allocator: zx.Allocator) zx.Component {
    const users = [_]struct { name: []const u8, role: []const u8 }{
        .{ .name = "Alice", .role = "Admin" },
        .{ .name = "Bob", .role = "Member" },
        .{ .name = "Charlie", .role = "Guest" },
    };

    return (
        <main @allocator={allocator}>
            <h2>Team Members</h2>
            <ul>
                {for (users) |user| (
                    <li>
                        <strong>{user.name}</strong>
                        <span> - {user.role}</span>
                    </li>
                )}
            </ul>
        </main>
    );
}

const zx = @import("zx");
<main><h2>Team Members</h2><ul><li><strong>Alice</strong><span> - Admin</span></li><li><strong>Bob</strong><span> - Member</span></li><li><strong>Charlie</strong><span> - Guest</span></li></ul></main>

Component Props

Create reusable components by defining props structs. Components can accept any Zig type as props, including booleans, strings, and custom structs.

pub fn Page(allocator: zx.Allocator) zx.Component {
    return (
        <main @allocator={allocator}>
            <h2>Product Cards</h2>
            <div style="display: flex; gap: 1rem; flex-wrap: wrap;">
                <ProductCard name="Laptop" price="$999" in_stock={true} />
                <ProductCard name="Phone" price="$699" in_stock={true} />
                <ProductCard name="Tablet" price="$499" in_stock={false} />
            </div>
        </main>
    );
}

const ProductCardProps = struct {
    name: []const u8,
    price: []const u8,
    in_stock: bool,
};

fn ProductCard(allocator: zx.Allocator, props: ProductCardProps) zx.Component {
    return (
        <div style="border: 1px solid #ccc; padding: 1rem; border-radius: 8px; min-width: 150px;" @allocator={allocator}>
            <h3>{props.name}</h3>
            <p style="font-size: 1.25rem; font-weight: bold;">{props.price}</p>
            {if (props.in_stock) (
                <span style="color: green;">In Stock</span>
            ) else (
                <span style="color: red;">Out of Stock</span>
            )}
        </div>
    );
}

const zx = @import("zx");
<main><h2>Product Cards</h2><div style="display: flex; gap: 1rem; flex-wrap: wrap;"><div style="border: 1px solid #ccc; padding: 1rem; border-radius: 8px; min-width: 150px;"><h3>Laptop</h3><p style="font-size: 1.25rem; font-weight: bold;">$999</p><span style="color: green;">In Stock</span></div><div style="border: 1px solid #ccc; padding: 1rem; border-radius: 8px; min-width: 150px;"><h3>Phone</h3><p style="font-size: 1.25rem; font-weight: bold;">$699</p><span style="color: green;">In Stock</span></div><div style="border: 1px solid #ccc; padding: 1rem; border-radius: 8px; min-width: 150px;"><h3>Tablet</h3><p style="font-size: 1.25rem; font-weight: bold;">$499</p><span style="color: red;">Out of Stock</span></div></div></main>

Dynamic Attributes

Set HTML attributes dynamically using Zig expressions. Boolean attributes like disabled and readonly are handled automatically.

pub fn Page(allocator: zx.Allocator) zx.Component {
    const is_dark = true;
    const size = 42;
    const user_name = "Alice";

    return (
        <main @allocator={allocator}>
            <h2>Dynamic Attributes</h2>
            <button class={if (is_dark) "btn-dark" else "btn-light"}>
                Click Me
            </button>
            <div style=`width: {size}px; height: {size}px; background: #3b82f6; border-radius: 4px;`> </div>
            <p>Hello, <span id={user_name}>{user_name}</span>!</p>
        </main>
    );
}

const std = @import("std");
const zx = @import("zx");
<main><h2>Dynamic Attributes</h2><button class="btn-dark"> Click Me</button><div style="width: 42px; height: 42px; background: #3b82f6; border-radius: 4px;"></div><p>Hello, <span id="Alice">Alice</span>!</p></main>

Form Handling

Build forms with standard HTML form elements. ZX handles form submission through standard HTTP methods.

pub fn Page(allocator: zx.Allocator) zx.Component {
    return (
        <main @allocator={allocator}>
            <h2>Contact Form</h2>
            <form method="post" action="/api/contact">
                <div>
                    <label for="name">Name</label>
                    <input type="text" id="name" name="name" placeholder="Your name" />
                </div>
                <div>
                    <label for="email">Email</label>
                    <input type="email" id="email" name="email" placeholder="your@email.com" />
                </div>
                <div>
                    <label for="message">Message</label>
                    <textarea id="message" name="message" placeholder="Your message..."></textarea>
                </div>
                <button type="submit">Send Message</button>
            </form>
        </main>
    );
}

const zx = @import("zx");
<main><h2>Contact Form</h2><form method="post" action="/api/contact"><div><label for="name">Name</label><input type="text" id="name" name="name" placeholder="Your name"></div><div><label for="email">Email</label><input type="email" id="email" name="email" placeholder="your@email.com"></div><div><label for="message">Message</label><textarea id="message" name="message" placeholder="Your message..."></textarea></div><button type="submit">Send Message</button></form></main>

Caching Alpha

ZX provides built-in caching at multiple levels to improve performance. Cache rendered HTML and serve it directly without re-rendering.

Component Caching

Use the @caching attribute to cache individual components. Specify duration with units: s (seconds), m (minutes), h (hours), or d (days). Add a key after : for manual invalidation.

component_caching.zx
// Cache a component for 10 seconds
fn Page(allocator: zx.Allocator) zx.Component {
    return (
        <div @allocator={allocator}>
            <Header @caching="1h" />
            <Content />
            <Sidebar @caching="30m:sidebar" />
        </div>
    );
}

fn Header(allocator: zx.Allocator) zx.Component {
    return (<header @allocator={allocator}>Site Header</header>);
}

fn Sidebar(allocator: zx.Allocator) zx.Component {
    return (<aside @allocator={allocator}>Sidebar</aside>);
}

Page Caching

Cache entire pages by exporting options with caching configuration. The server automatically adds ETag and Cache-Control headers.

cached_page.zx
pub fn Page(ctx: zx.PageContext) zx.Component {
    return (
        <div @allocator={ctx.arena}>
            <h1>Cached Page</h1>
            <p>This entire page is cached for 5 minutes.</p>
            <p>Current time: {std.time.timestamp()}</p>
        </div>
    );
}

// Page-level caching configuration
pub const options = zx.PageOptions{
    .caching = .{ .seconds = 300 }, // 5 minutes
};

Layout Caching

Layouts can also be cached. Be careful: this caches the entire layout including page content.

cached_layout.zx
pub fn Layout(ctx: zx.LayoutContext, children: zx.Component) zx.Component {
    return (
        <html @allocator={ctx.arena}>
            <head><title>My Site</title></head>
            <body>
                <nav>Navigation</nav>
                <main>{children}</main>
                <footer>Footer</footer>
            </body>
        </html>
    );
}

// Layout caching (caches entire layout including children)
pub const options = zx.LayoutOptions{
    .caching = .{ .seconds = 3600 }, // 1 hour
};
Tip: Use zx.cache.del("cmp:key") or zx.cache.delPrefix("cmp:prefix") to manually invalidate cached content when data changes.