ZX Framework
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>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.
// 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.
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.
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
};zx.cache.del("cmp:key") or zx.cache.delPrefix("cmp:prefix") to manually invalidate cached content when data changes.