ZX - Fast Web Framework for Zig

Introduction

ZX is a Zig library for building web applications with JSX-like syntax. Write declarative UI components using familiar JSX patterns, transpiled to efficient Zig code.

ZX combines the power and performance of Zig with the expressiveness of JSX, enabling you to build fast, type-safe web applications. ZX is significantly faster than frameworks like Next.js at SSR. Currently 120X faster than Next.js at SSR.

Getting Started

Installation

Install the ZX CLI tool to get started with building ZX applications.

Linux/macOS

curl -fsSL https://ziex.dev/install | bash

Windows

powershell -c "irm ziex.dev/install.ps1 | iex"

Installing Zig

brew install zig # macOS
winget install -e --id zig.zig # Windows

See for other platforms โ†’

Creating a New Project

Create a new folder for your project, navigate into it, and run zx init:

mkdir my-project
cd my-project
zx init

Make sure to run zx init inside a clean project folder. After running the command, you'll see:

โ—‹ Initializing ZX project!  Template: default

  + .vscode/extensions.json
  + build.zig.zon
  + build.zig
  + README.md
  + site/assets/style.css
  + site/main.zig
  + site/pages/about/page.zx
  + site/pages/layout.zx
  + site/pages/page.zx
  + src/root.zig
  + .gitignore

Now run โ†’ 

zig build serve

Then run the development server:

zig build serve

You'll see the server start:

ZX ยท 0.0.1-dev.90
  - Local: http://localhost:3000

Editor Setup

Enhance your development experience with the official ZX extension for VSCode and Cursor. The extension provides syntax highlighting, code completion, and other helpful features for working with .zx files. Install it from Open VSX.

Expressions

ZX provides different expression syntaxes for embedding dynamic content. Each syntax serves a specific purpose: rendering components, displaying text safely, or formatting values with custom format specifiers.

Text Expressions

Use {expression} to embed text content. All text is automatically HTML-escaped to prevent XSS attacks. This is the safe way to display user input or dynamic text content.

ZX
<main @allocator={allocator}>
    <section>
        <p>User: {user_name}</p>
        <p>Safe HTML: {html_content}</p>
        <p>Unsafe HTML: {[unsafe_html:s]}</p>
    </section>
</main>
<main><section><p>User: Alice &amp; Bob</p><p>Safe HTML: &lt;script&gt;alert(&#x27;XSS&#x27;)&lt;/script&gt;</p><p>Unsafe HTML: <span>Test</span></p></section></main>
_zx.zx(
        .main,
        .{
            .allocator = allocator,
            .children = &.{
                _zx.zx(
                    .section,
                    .{
                        .children = &.{
                            _zx.zx(
                                .p,
                                .{
                                    .children = &.{
                                        _zx.txt("User: "),
                                        _zx.txt(user_name),
                                    },
                                },
                            ),
                            _zx.zx(
                                .p,
                                .{
                                    .children = &.{
                                        _zx.txt("Safe HTML: "),
                                        _zx.txt(html_content),
                                    },
                                },
                            ),
                            _zx.zx(
                                .p,
                                .{
                                    .children = &.{
                                        _zx.txt("Unsafe HTML: "),
                                        _zx.fmt("{s}", .{unsafe_html}),
                                    },
                                },
                            ),
                        },
                    },
                ),
            },
        },
    )

Format Expressions

Use {[expression:format]} to format values with custom format specifiers. The format string follows Zig's standard format specifier syntax (e.g., d for decimal, x for hexadecimal). Unlike text expressions, format expressions are not HTML-escaped, making them suitable for numeric formatting and other non-HTML content.

ZX
<section @allocator={allocator}>
    <p>Count: {[count:d]}</p>
    <p>Hex: 0x{[hex_value:x]}</p>
    <p>Percentage: {[percentage:d]}%</p>
</section>
<section><p>Count: 42</p><p>Hex: 0xff</p><p>Percentage: 75%</p></section>
_zx.zx(
        .section,
        .{
            .allocator = allocator,
            .children = &.{
                _zx.zx(
                    .p,
                    .{
                        .children = &.{
                            _zx.txt("Count: "),
                            _zx.fmt("{d}", .{count}),
                        },
                    },
                ),
                _zx.zx(
                    .p,
                    .{
                        .children = &.{
                            _zx.txt("Hex: 0x"),
                            _zx.fmt("{x}", .{hex_value}),
                        },
                    },
                ),
                _zx.zx(
                    .p,
                    .{
                        .children = &.{
                            _zx.txt("Percentage: "),
                            _zx.fmt("{d}", .{percentage}),
                            _zx.txt("%"),
                        },
                    },
                ),
            },
        },
    )

Component Expressions

Use {(expression)} to embed a component directly. The expression must evaluate to a zx.Component type. This is useful when you want to conditionally render or reuse components dynamically.

ZX
<section @allocator={allocator}>
    {(greeting)}
</section>
<section>Hello!</section>
_zx.zx(
        .section,
        .{
            .allocator = allocator,
            .children = &.{
                greeting,
            },
        },
    )

Control Flow

ZX supports conditional rendering and iteration using familiar Zig control flow constructs. These expressions allow you to conditionally render components or iterate over collections to build dynamic UIs.

If Statements

Use if expressions to conditionally render components based on boolean conditions. The else branch is optional and can render alternative content when the condition is false.

ZX
<main @allocator={allocator}>
    <section>
        {if (is_admin) (<p>Admin</p>) else (<p>User</p>)}
    </section>
    <section>
        {if (is_admin) ("Powerful") else ("Powerless")}
    </section>
    <section>
        {if (is_logged_in) (
            <p>Welcome, User!</p>
        ) else (
            <p>Please log in to continue.</p>
        )}
    </section>
</main>
<main><section><p>Admin</p></section><section><fragment>&quot;Powerful&quot;</fragment></section><section><p>Please log in to continue.</p></section></main>
_zx.zx(
        .main,
        .{
            .allocator = allocator,
            .children = &.{
                _zx.zx(
                    .section,
                    .{
                        .children = &.{
                            if ((is_admin)) _zx.zx(
                                .p,
                                .{
                                    .children = &.{
                                        _zx.txt("Admin"),
                                    },
                                },
                            ) else _zx.zx(
                                .p,
                                .{
                                    .children = &.{
                                        _zx.txt("User"),
                                    },
                                },
                            ),
                        },
                    },
                ),
                _zx.zx(
                    .section,
                    .{
                        .children = &.{
                            if ((is_admin)) _zx.zx(
                                .fragment,
                                .{
                                    .children = &.{
                                        _zx.txt("\"Powerful\""),
                                    },
                                },
                            ) else _zx.zx(
                                .fragment,
                                .{
                                    .children = &.{
                                        _zx.txt("\"Powerless\""),
                                    },
                                },
                            ),
                        },
                    },
                ),
                _zx.zx(
                    .section,
                    .{
                        .children = &.{
                            if ((is_logged_in)) _zx.zx(
                                .p,
                                .{
                                    .children = &.{
                                        _zx.txt("Welcome, User!"),
                                    },
                                },
                            ) else _zx.zx(
                                .p,
                                .{
                                    .children = &.{
                                        _zx.txt("Please log in to continue."),
                                    },
                                },
                            ),
                        },
                    },
                ),
            },
        },
    )

Switch Statements

Use switch expressions to match against enum values or other types. Each case can return either a string literal or a component. Switch expressions are particularly useful for rendering different UI based on state or user roles.

ZX
<main @allocator={allocator}>
    <section>
        {switch (user_swtc.user_type) {
            .admin => ("Admin"),
            .member => ("Member"),
        }}
    </section>
    <section>
        {switch (user_swtc.user_type) {
            .admin => (<p>Powerful</p>),
            .member => (<p>Powerless</p>),
        }}
    </section>
</main>
<main><section>Admin</section><section><p>Powerful</p></section></main>
_zx.zx(
        .main,
        .{
            .allocator = allocator,
            .children = &.{
                _zx.zx(
                    .section,
                    .{
                        .children = &.{
                            switch (user_swtc.user_type) {
                                .admin => _zx.txt("Admin"),
                                .member => _zx.txt("Member"),
                            },
                        },
                    },
                ),
                _zx.zx(
                    .section,
                    .{
                        .children = &.{
                            switch (user_swtc.user_type) {
                                .admin => _zx.zx(
                                    .p,
                                    .{
                                        .children = &.{
                                            _zx.txt("Powerful"),
                                        },
                                    },
                                ),
                                .member => _zx.zx(
                                    .p,
                                    .{
                                        .children = &.{
                                            _zx.txt("Powerless"),
                                        },
                                    },
                                ),
                            },
                        },
                    },
                ),
            },
        },
    )

For Loops

Use for loops to iterate over arrays, slices, or strings and render a component for each item. The loop variable can be used within the component body to display item-specific content. This is ideal for rendering lists, tables, or any repeating UI patterns.

ZX
<main @allocator={ctx}>
    {for (users) |user| (
      <div>
        <p>{user.name}</p>
        {switch (user.role) {
            .admin => (<span>Admin</span>),
            .member => (<span>Member</span>),
            .guest => (<span>Guest</span>),
        }}
      </div>
    )}
</main>
<main><div><p>John</p><span>Admin</span></div><div><p>Jane</p><span>Member</span></div><div><p>Jim</p><span>Guest</span></div></main>
_zx.zx(
        .main,
        .{
            .allocator = ctx,
            .children = blk: {
                const __zx_children = _zx.getAllocator().alloc(zx.Component, users.len) catch unreachable;
                for (users, 0..) |user, _zx_i| {
                    __zx_children[_zx_i] = _zx.zx(
                        .div,
                        .{
                            .children = &.{
                                _zx.zx(
                                    .p,
                                    .{
                                        .children = &.{
                                            _zx.txt(user.name),
                                        },
                                    },
                                ),
                                switch (user.role) {
                                    .admin => _zx.zx(
                                        .span,
                                        .{
                                            .children = &.{
                                                _zx.txt("Admin"),
                                            },
                                        },
                                    ),
                                    .member => _zx.zx(
                                        .span,
                                        .{
                                            .children = &.{
                                                _zx.txt("Member"),
                                            },
                                        },
                                    ),
                                    .guest => _zx.zx(
                                        .span,
                                        .{
                                            .children = &.{
                                                _zx.txt("Guest"),
                                            },
                                        },
                                    ),
                                },
                            },
                        },
                    );
                }
                break :blk __zx_children;
            },
        },
    )

Components

Components are reusable functions that return zx.Component. They allow you to encapsulate UI logic and create modular, maintainable code. Components can accept props (properties) to customize their behavior and appearance.

Component Function Signatures

Component functions must follow one of two signatures:

  • Single parameter (allocator only):fn ComponentName(allocator: zx.Allocator) zx.Component
  • Two parameters (allocator and props):fn ComponentName(allocator: zx.Allocator, props: PropsType) zx.Component

Defining Components

To create a component, define a function that returns a zx.Component. The function must have an allocator as its first parameter. If your component needs configuration, add a props struct as the second parameter:

ZX
<main @allocator={allocator}>
    <Button title="Submit" class="primary-btn" />
    <Button title="Cancel" />
    <Button />
</main>
<main><button class="primary-btn">Submit</button><button class="btn">Cancel</button><button class="btn">Click Me</button></main>
_zx.zx(
        .main,
        .{
            .allocator = allocator,
            .children = &.{
                _zx.lazy(Button, .{ .title = "Submit", .class = "primary-btn" }),
                _zx.lazy(Button, .{ .title = "Cancel" }),
                _zx.lazy(Button, .{}),
            },
        },
    )

Props Coercion

ZX automatically coerces the attributes you pass to a component into the expected props struct. Missing required fields will result in a compile-time error, while fields with default values are optional. This provides type safety while keeping component usage flexible.

Routing

ZX provides a file-based routing system that maps URL paths to page components. Routes can be nested, and layouts can be applied hierarchically to wrap pages with common UI elements.

Pages

Pages are the main content components for each route. A page function must:

  • Accept a zx.PageContext as its only parameter
  • Return a zx.Component
  • Be exported as pub fn Page
ZX
<main @allocator={allocator}>
    <h1>Hello, World!</h1>
    <p>Welcome to the page</p>
</main>
<main><h1>Hello, World!</h1><p>Welcome to the page</p></main>
_zx.zx(
        .main,
        .{
            .allocator = allocator,
            .children = &.{
                _zx.zx(
                    .h1,
                    .{
                        .children = &.{
                            _zx.txt("Hello, World!"),
                        },
                    },
                ),
                _zx.zx(
                    .p,
                    .{
                        .children = &.{
                            _zx.txt("Welcome to the page"),
                        },
                    },
                ),
            },
        },
    )

Layouts

Layouts wrap pages with common UI elements like headers, footers, or navigation. A layout function must:

  • Accept a zx.LayoutContext as the first parameter
  • Accept a zx.Component (the page content) as the second parameter
  • Return a zx.Component
  • Be exported as pub fn Layout
ZX
<div @allocator={ctx.arena}>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.30.0/themes/prism.min.css" rel="stylesheet">
    <link rel="stylesheet" href={css_version}>
    <header class="top-bar">
        <div class="top-bar-content">
            <div class="top-bar-left">
                <a href="/" class="top-bar-logo">ZX</a>
            </div>
            <div class="top-bar-right">
                <nav class="top-bar-nav">
                    <a href="/docs" class="top-bar-link">Docs</a>
                    <a href="/examples" class="top-bar-link">Examples</a>
                    <a href="/cli" class="top-bar-link">CLI</a>
                </nav>
                <div class="top-bar-actions">
                    <a href="https://github.com/nurulhudaapon/zx" target="_blank" rel="noopener noreferrer" class="top-bar-icon" aria-label="GitHub" title="v{zx.info.version_string}">
                        <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
                            <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>
                        </svg>
                    </a>
                </div>
            </div>
        </div>
    </header>
        
    <div class="container examples-container">
        <main class="content examples-content">
            {(children)}
        </main>
    </div>

    <script async type="module" src={js_version}></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.30.0/components/prism-core.min.js"/>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.30.0/plugins/autoloader/prism-autoloader.min.js"/>
</div>
<div><link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.30.0/themes/prism.min.css" rel="stylesheet"><link rel="stylesheet" href="/assets/docs.css?v=0.0.1-dev.242"><header class="top-bar"><div class="top-bar-content"><div class="top-bar-left"><a href="/" class="top-bar-logo">ZX</a></div><div class="top-bar-right"><nav class="top-bar-nav"><a href="/docs" class="top-bar-link">Docs</a><a href="/examples" class="top-bar-link">Examples</a><a href="/cli" class="top-bar-link">CLI</a></nav><div class="top-bar-actions"><a href="https://github.com/nurulhudaapon/zx" target="_blank" rel="noopener noreferrer" class="top-bar-icon" aria-label="GitHub" title="v{zx.info.version_string}"><svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
                                    <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>
                                </svg></svg></a></div></div></div></header><div class="container examples-container"><main class="content examples-content"><main><h1>Hello, World!</h1><p>Welcome to the page</p></main></main></div><script type="module" src="/assets/docs.js?v=0.0.1-dev.242"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.30.0/components/prism-core.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.30.0/plugins/autoloader/prism-autoloader.min.js"></script></div>
_zx.zx(
        .div,
        .{
            .allocator = ctx.arena,
            .children = &.{
                _zx.zx(
                    .link,
                    .{
                        .attributes = &.{
                            .{ .name = "href", .value = "https://cdnjs.cloudflare.com/ajax/libs/prism/1.30.0/themes/prism.min.css" },
                            .{ .name = "rel", .value = "stylesheet" },
                        },
                    },
                ),
                _zx.zx(
                    .link,
                    .{
                        .attributes = &.{
                            .{ .name = "rel", .value = "stylesheet" },
                            .{ .name = "href", .value = css_version },
                        },
                    },
                ),
                _zx.zx(
                    .header,
                    .{
                        .attributes = &.{
                            .{ .name = "class", .value = "top-bar" },
                        },
                        .children = &.{
                            _zx.zx(
                                .div,
                                .{
                                    .attributes = &.{
                                        .{ .name = "class", .value = "top-bar-content" },
                                    },
                                    .children = &.{
                                        _zx.zx(
                                            .div,
                                            .{
                                                .attributes = &.{
                                                    .{ .name = "class", .value = "top-bar-left" },
                                                },
                                                .children = &.{
                                                    _zx.zx(
                                                        .a,
                                                        .{
                                                            .attributes = &.{
                                                                .{ .name = "href", .value = "/" },
                                                                .{ .name = "class", .value = "top-bar-logo" },
                                                            },
                                                            .children = &.{
                                                                _zx.txt("ZX"),
                                                            },
                                                        },
                                                    ),
                                                },
                                            },
                                        ),
                                        _zx.zx(
                                            .div,
                                            .{
                                                .attributes = &.{
                                                    .{ .name = "class", .value = "top-bar-right" },
                                                },
                                                .children = &.{
                                                    _zx.zx(
                                                        .nav,
                                                        .{
                                                            .attributes = &.{
                                                                .{ .name = "class", .value = "top-bar-nav" },
                                                            },
                                                            .children = &.{
                                                                _zx.zx(
                                                                    .a,
                                                                    .{
                                                                        .attributes = &.{
                                                                            .{ .name = "href", .value = "/docs" },
                                                                            .{ .name = "class", .value = "top-bar-link" },
                                                                        },
                                                                        .children = &.{
                                                                            _zx.txt("Docs"),
                                                                        },
                                                                    },
                                                                ),
                                                                _zx.zx(
                                                                    .a,
                                                                    .{
                                                                        .attributes = &.{
                                                                            .{ .name = "href", .value = "/examples" },
                                                                            .{ .name = "class", .value = "top-bar-link" },
                                                                        },
                                                                        .children = &.{
                                                                            _zx.txt("Examples"),
                                                                        },
                                                                    },
                                                                ),
                                                                _zx.zx(
                                                                    .a,
                                                                    .{
                                                                        .attributes = &.{
                                                                            .{ .name = "href", .value = "/cli" },
                                                                            .{ .name = "class", .value = "top-bar-link" },
                                                                        },
                                                                        .children = &.{
                                                                            _zx.txt("CLI"),
                                                                        },
                                                                    },
                                                                ),
                                                            },
                                                        },
                                                    ),
                                                    _zx.zx(
                                                        .div,
                                                        .{
                                                            .attributes = &.{
                                                                .{ .name = "class", .value = "top-bar-actions" },
                                                            },
                                                            .children = &.{
                                                                _zx.zx(
                                                                    .a,
                                                                    .{
                                                                        .attributes = &.{
                                                                            .{ .name = "href", .value = "https://github.com/nurulhudaapon/zx" },
                                                                            .{ .name = "target", .value = "_blank" },
                                                                            .{ .name = "rel", .value = "noopener noreferrer" },
                                                                            .{ .name = "class", .value = "top-bar-icon" },
                                                                            .{ .name = "aria-label", .value = "GitHub" },
                                                                            .{ .name = "title", .value = "v{zx.info.version_string}" },
                                                                        },
                                                                        .children = &.{
                                                                            _zx.zx(
                                                                                .svg,
                                                                                .{
                                                                                    .attributes = &.{
                                                                                        .{ .name = "width", .value = "16" },
                                                                                        .{ .name = "height", .value = "16" },
                                                                                        .{ .name = "viewBox", .value = "0 0 16 16" },
                                                                                        .{ .name = "fill", .value = "currentColor" },
                                                                                    },
                                                                                    .children = &.{
                                                                                        _zx.fmt("{s}", .{"\n                                    <path d=\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z\"/>\n                                </svg>"}),
                                                                                    },
                                                                                },
                                                                            ),
                                                                        },
                                                                    },
                                                                ),
                                                            },
                                                        },
                                                    ),
                                                },
                                            },
                                        ),
                                    },
                                },
                            ),
                        },
                    },
                ),
                _zx.zx(
                    .div,
                    .{
                        .attributes = &.{
                            .{ .name = "class", .value = "container examples-container" },
                        },
                        .children = &.{
                            _zx.zx(
                                .main,
                                .{
                                    .attributes = &.{
                                        .{ .name = "class", .value = "content examples-content" },
                                    },
                                    .children = &.{
                                        children,
                                    },
                                },
                            ),
                        },
                    },
                ),
                _zx.zx(
                    .script,
                    .{
                        .attributes = &.{
                            .{ .name = "type", .value = "module" },
                            .{ .name = "src", .value = js_version },
                        },
                    },
                ),
                _zx.zx(
                    .script,
                    .{
                        .attributes = &.{
                            .{ .name = "src", .value = "https://cdnjs.cloudflare.com/ajax/libs/prism/1.30.0/components/prism-core.min.js" },
                        },
                    },
                ),
                _zx.zx(
                    .script,
                    .{
                        .attributes = &.{
                            .{ .name = "src", .value = "https://cdnjs.cloudflare.com/ajax/libs/prism/1.30.0/plugins/autoloader/prism-autoloader.min.js" },
                        },
                    },
                ),
            },
        },
    )

PageContext and LayoutContext

Both PageContext and LayoutContext provide access to:

  • request: The HTTP request object with headers, query params, body, etc.
  • response: The HTTP response object for setting headers and writing the response
  • allocator: Global allocator for persistent allocations (freed manually)
  • arena: Request-scoped allocator that's automatically freed after the request (recommended for most use cases)

Use ctx.arena for temporary allocations that only need to persist during request processing. This is the recommended allocator for most page and component code.

Builtin Attributes

ZX provides special builtin attributes that control component behavior and memory management. These attributes are prefixed with @ to distinguish them from regular HTML attributes.

@allocator

The @allocator attribute is required to be passed to the topmost component. All child components will inherit the allocator from the parent component. It is available to all child components and expressions within that component tree.

Why is @allocator needed?

ZX components can allocate memory for various purposes:

  • Storing text content (which is HTML-escaped and allocated)
  • Copying child components arrays
  • Copying attribute arrays
  • Formatting expressions that allocate formatted strings

Usage:

ZX
<html @allocator={ctx.arena}>
    <head>
        <title>My Page</title>
    </head>
    <body>
        <MyComponent />
    </body>
</html>
<html><head><title>My Page</title></head><body><div>This text will be allocated and escaped</div></body></html>
_zx.zx(
        .html,
        .{
            .allocator = ctx.arena,
            .children = &.{
                _zx.zx(
                    .head,
                    .{
                        .children = &.{
                            _zx.zx(
                                .title,
                                .{
                                    .children = &.{
                                        _zx.txt("My Page"),
                                    },
                                },
                            ),
                        },
                    },
                ),
                _zx.zx(
                    .body,
                    .{
                        .children = &.{
                            _zx.lazy(MyComponent, .{}),
                        },
                    },
                ),
            },
        },
    )

Best Practices:

  • Always use ctx.arena in page components (it's automatically freed after the request)
  • Pass the allocator parameter to custom components and use it in the @allocator attribute
  • Set @allocator on the root element of each component that needs memory allocation
  • Child components inherit the allocator from their parent's component, so you don't need to pass it explicitly to every nested component

Examples