Pugz

A Pug template engine for Zig, supporting both build-time compilation and runtime interpretation.

Features

  • Pug syntax (tags, classes, IDs, attributes)
  • Interpolation (#{var}, !{unescaped})
  • Conditionals (if, else if, else, unless)
  • Iteration (each, while)
  • Template inheritance (extends, block, append, prepend)
  • Includes
  • Mixins with parameters, defaults, rest args, and block content
  • Comments (rendered and unbuffered)

Installation

Add pugz as a dependency in your build.zig.zon:

zig fetch --save "git+https://github.com/ankitpatial/pugz#main"

Note: The primary repository is hosted at code.patial.tech. GitHub is a mirror. For dependencies, prefer the GitHub mirror for better availability.


Usage

Compiled Mode (Build-Time)

Templates are converted to native Zig code at build time. No parsing happens at runtime.

build.zig:

const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const pugz_dep = b.dependency("pugz", .{
        .target = target,
        .optimize = optimize,
    });

    const build_templates = @import("pugz").build_templates;
    const compiled_templates = build_templates.compileTemplates(b, .{
        .source_dir = "views",
    });

    const exe = b.addExecutable(.{
        .name = "myapp",
        .root_module = b.createModule(.{
            .root_source_file = b.path("src/main.zig"),
            .target = target,
            .optimize = optimize,
            .imports = &.{
                .{ .name = "pugz", .module = pugz_dep.module("pugz") },
                .{ .name = "tpls", .module = compiled_templates },
            },
        }),
    });

    b.installArtifact(exe);
}

Usage:

const std = @import("std");
const tpls = @import("tpls");

pub fn handleRequest(allocator: std.mem.Allocator) ![]u8 {
    var arena = std.heap.ArenaAllocator.init(allocator);
    defer arena.deinit();

    return try tpls.home(arena.allocator(), .{
        .title = "Welcome",
        .user = .{ .name = "Alice" },
        .items = &[_][]const u8{ "One", "Two", "Three" },
    });
}

Interpreted Mode (Runtime)

Templates are parsed and evaluated at runtime. Useful for development or dynamic templates.

const std = @import("std");
const pugz = @import("pugz");

pub fn main() !void {
    var engine = pugz.ViewEngine.init(.{
        .views_dir = "views",
    });

    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();

    const html = try engine.render(arena.allocator(), "index", .{
        .title = "Hello",
        .name = "World",
    });

    std.debug.print("{s}\n", .{html});
}

Inline template strings:

const html = try pugz.renderTemplate(allocator,
    \\h1 Hello, #{name}!
    \\ul
    \\  each item in items
    \\    li= item
, .{
    .name = "World",
    .items = &[_][]const u8{ "one", "two", "three" },
});

With http.zig

fn handler(_: *App, _: *httpz.Request, res: *httpz.Response) !void {
    // Compiled mode
    const html = try tpls.home(res.arena, .{
        .title = "Hello",
    });

    res.content_type = .HTML;
    res.body = html;
}

Memory Management

Always use an ArenaAllocator for rendering. Template rendering creates many small allocations that should be freed together.

var arena = std.heap.ArenaAllocator.init(allocator);
defer arena.deinit();

const html = try engine.render(arena.allocator(), "index", data);

Documentation


Benchmarks

Same templates and data (src/benchmarks/templates/), MacBook Air M2, 2000 iterations, best of 5 runs.

Template Pug.js Pugz Compiled Diff Pugz Interpreted Diff
simple-0 0.4ms 0.1ms +4x 0.4ms 1x
simple-1 1.3ms 0.6ms +2.2x 5.8ms -4.5x
simple-2 1.6ms 0.5ms +3.2x 4.6ms -2.9x
if-expression 0.5ms 0.2ms +2.5x 4.1ms -8.2x
projects-escaped 4.2ms 0.6ms +7x 5.8ms -1.4x
search-results 14.7ms 5.3ms +2.8x 50.7ms -3.4x
friends 145.5ms 50.4ms +2.9x 450.8ms -3.1x
  • Pug.js and Pugz Compiled: render-only (pre-compiled)
  • Pugz Interpreted: parse + render on each iteration
  • Diff: +Nx = N times faster, -Nx = N times slower

Development

zig build test                # Run all tests
zig build bench-compiled      # Benchmark compiled mode
zig build bench-interpreted   # Benchmark interpreted mode

# Pug.js benchmark (for comparison)
cd src/benchmarks/pugjs && npm install && npm run bench

License

MIT

Description
No description provided
Readme MIT 2.2 MiB
Languages
Zig 74.1%
JavaScript 11.9%
Pug 9.3%
HTML 4.7%