2026-01-24 14:31:24 +05:30
*Yet not ready to use in production, i tried to get it done using Cluade but its not quite there where i want it*
*So i will try it by my self keeping PugJS version as a reference*
2026-01-17 23:59:22 +05:30
# Pugz
2026-01-22 11:08:52 +05:30
A Pug template engine for Zig, supporting both build-time compilation and runtime interpretation.
2026-01-17 23:59:22 +05:30
## 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
2026-01-18 19:58:22 +05:30
Add pugz as a dependency in your `build.zig.zon` :
2026-01-17 23:59:22 +05:30
2026-01-18 19:58:22 +05:30
```bash
2026-01-20 18:08:42 +05:30
zig fetch --save "git+https://github.com/ankitpatial/pugz#main "
2026-01-17 23:59:22 +05:30
```
2026-01-20 18:08:42 +05:30
> **Note:** The primary repository is hosted at `code.patial.tech`. GitHub is a mirror. For dependencies, prefer the GitHub mirror for better availability.
2026-01-22 11:08:52 +05:30
---
2026-01-20 18:08:42 +05:30
2026-01-22 11:08:52 +05:30
## Usage
### Compiled Mode (Build-Time)
Templates are converted to native Zig code at build time. No parsing happens at runtime.
**build.zig:**
2026-01-17 23:59:22 +05:30
```zig
2026-01-22 11:08:52 +05:30
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);
}
2026-01-17 23:59:22 +05:30
```
2026-01-22 11:08:52 +05:30
**Usage:**
2026-01-17 23:59:22 +05:30
2026-01-22 11:08:52 +05:30
```zig
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.
2026-01-19 12:11:45 +05:30
2026-01-17 23:59:22 +05:30
```zig
const std = @import ("std");
const pugz = @import ("pugz");
pub fn main() !void {
2026-01-22 11:08:52 +05:30
var engine = pugz.ViewEngine.init(.{
2026-01-17 23:59:22 +05:30
.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});
}
```
2026-01-22 11:08:52 +05:30
**Inline template strings:**
2026-01-17 23:59:22 +05:30
```zig
2026-01-22 11:08:52 +05:30
const html = try pugz.renderTemplate(allocator,
2026-01-17 23:59:22 +05:30
\\h1 Hello, #{name}!
\\ul
\\ each item in items
\\ li= item
, .{
.name = "World",
.items = & [_][]const u8{ "one", "two", "three" },
});
```
2026-01-22 11:08:52 +05:30
---
2026-01-18 00:51:31 +05:30
2026-01-22 11:08:52 +05:30
### With http.zig
2026-01-18 00:51:31 +05:30
2026-01-22 11:08:52 +05:30
```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;
}
2026-01-18 00:51:31 +05:30
```
2026-01-22 11:08:52 +05:30
---
2026-01-18 00:51:31 +05:30
2026-01-22 11:08:52 +05:30
## Memory Management
Always use an `ArenaAllocator` for rendering. Template rendering creates many small allocations that should be freed together.
2026-01-18 00:51:31 +05:30
2026-01-22 11:08:52 +05:30
```zig
var arena = std.heap.ArenaAllocator.init(allocator);
defer arena.deinit();
const html = try engine.render(arena.allocator(), "index", data);
2026-01-17 23:59:22 +05:30
```
2026-01-22 11:08:52 +05:30
---
## Documentation
- [Template Syntax ](docs/syntax.md ) - Complete syntax reference
- [API Reference ](docs/api.md ) - Detailed API documentation
---
2026-01-17 23:59:22 +05:30
## Benchmarks
2026-01-22 11:08:52 +05:30
Same templates and data (`src/benchmarks/templates/` ), MacBook Air M2, 2000 iterations, best of 5 runs.
2026-01-18 00:51:31 +05:30
2026-01-22 11:08:52 +05:30
| 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 |
2026-01-18 00:51:31 +05:30
2026-01-22 11:08:52 +05:30
- 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
2026-01-18 00:51:31 +05:30
2026-01-22 11:08:52 +05:30
---
## Development
2026-01-18 00:51:31 +05:30
2026-01-22 11:08:52 +05:30
```bash
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
```
2026-01-17 23:59:22 +05:30
2026-01-22 11:08:52 +05:30
---
2026-01-17 23:59:22 +05:30
## License
MIT