2026-01-17 23:59:22 +05:30
# Pugz
2026-01-25 15:32:38 +05:30
A Pug template engine written in Zig. Templates are parsed once and cached, then rendered with data at runtime.
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)
2026-01-25 15:23:57 +05:30
- Pretty printing with indentation
- LRU cache with configurable size and TTL
2026-01-17 23:59:22 +05:30
## 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-25 15:32:38 +05:30
Then in your `build.zig` :
2026-01-17 23:59:22 +05:30
2026-01-22 11:08:52 +05:30
```zig
2026-01-25 15:32:38 +05:30
const pugz_dep = b.dependency("pugz", .{
.target = target,
.optimize = optimize,
});
2026-01-22 11:08:52 +05:30
2026-01-25 15:32:38 +05:30
exe.root_module.addImport("pugz", pugz_dep.module("pugz"));
2026-01-22 11:08:52 +05:30
```
---
2026-01-25 15:32:38 +05:30
## Usage
### ViewEngine (Recommended)
2026-01-22 11:08:52 +05:30
2026-01-25 15:32:38 +05:30
The `ViewEngine` provides template caching and file-based template management for web servers.
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-25 15:23:57 +05:30
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
2026-01-25 15:32:38 +05:30
// Initialize once at server startup
2026-01-25 15:23:57 +05:30
var engine = try pugz.ViewEngine.init(allocator, .{
2026-01-17 23:59:22 +05:30
.views_dir = "views",
});
2026-01-25 15:23:57 +05:30
defer engine.deinit();
2026-01-17 23:59:22 +05:30
2026-01-25 15:32:38 +05:30
// Per-request rendering with arena allocator
2026-01-25 15:23:57 +05:30
var arena = std.heap.ArenaAllocator.init(allocator);
2026-01-17 23:59:22 +05:30
defer arena.deinit();
2026-01-25 15:32:38 +05:30
const html = try engine.render(arena.allocator(), "pages/index", .{
2026-01-17 23:59:22 +05:30
.title = "Hello",
.name = "World",
});
std.debug.print("{s}\n", .{html});
}
```
2026-01-25 15:32:38 +05:30
### Inline Templates
For simple use cases or testing, render template strings directly:
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-25 15:32:38 +05:30
### With http.zig
```zig
const pugz = @import ("pugz");
const httpz = @import ("httpz");
var engine: pugz.ViewEngine = undefined;
pub fn main() !void {
engine = try pugz.ViewEngine.init(allocator, .{
.views_dir = "views",
});
defer engine.deinit();
var server = try httpz.Server(*Handler).init(allocator, .{}, handler);
try server.listen();
}
fn handler(_: *Handler, _: *httpz.Request, res: *httpz.Response) !void {
res.content_type = .HTML;
res.body = try engine.render(res.arena, "pages/home", .{
.title = "Hello",
.user = .{ .name = "Alice" },
});
}
```
2026-01-22 11:08:52 +05:30
---
2026-01-18 00:51:31 +05:30
2026-01-25 15:32:38 +05:30
## ViewEngine Options
2026-01-25 15:23:57 +05:30
```zig
var engine = try pugz.ViewEngine.init(allocator, .{
.views_dir = "views", // Root directory for templates
.extension = ".pug", // File extension (default: .pug)
.pretty = false, // Enable pretty-printed output
.cache_enabled = true, // Enable AST caching
.max_cached_templates = 100, // LRU cache size (0 = unlimited)
.cache_ttl_seconds = 5, // Cache TTL for development (0 = never expires)
});
```
| Option | Default | Description |
|--------|---------|-------------|
| `views_dir` | `"views"` | Root directory containing templates |
| `extension` | `".pug"` | File extension for templates |
| `pretty` | `false` | Enable pretty-printed HTML with indentation |
| `cache_enabled` | `true` | Enable AST caching for performance |
| `max_cached_templates` | `0` | Max templates in LRU cache (0 = unlimited hashmap) |
| `cache_ttl_seconds` | `0` | Cache TTL in seconds (0 = never expires) |
---
2026-01-22 11:08:52 +05:30
## Memory Management
2026-01-25 15:32:38 +05:30
Always use an `ArenaAllocator` for rendering. Template rendering creates many small allocations that should be freed together after the response is sent.
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
2026-01-25 15:32:38 +05:30
- [Template Syntax ](src/docs/syntax.md ) - Complete syntax reference
- [API Reference ](src/docs/api.md ) - Detailed API documentation
2026-01-22 11:08:52 +05:30
---
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-25 15:32:38 +05:30
Both Pug.js and Pugz parse templates once, then measure render-only time.
2026-01-18 00:51:31 +05:30
2026-01-25 15:32:38 +05:30
| Template | Pug.js | Pugz | Speedup |
|----------|--------|------|---------|
| simple-0 | 0.8ms | 0.2ms | 4x |
| simple-1 | 1.5ms | 0.9ms | 1.7x |
| simple-2 | 1.7ms | 2.4ms | 0.7x |
| if-expression | 0.6ms | 0.4ms | 1.5x |
| projects-escaped | 4.6ms | 2.4ms | 1.9x |
| search-results | 15.3ms | 17.7ms | 0.9x |
| friends | 156.7ms | 132.2ms | 1.2x |
| **TOTAL** | **181.3ms** | **156.2ms** | **1.16x** |
Run benchmarks:
```bash
# Pugz
zig build bench
# Pug.js (for comparison)
cd src/benchmarks/pugjs & & npm install & & npm run bench
```
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
2026-01-25 15:32:38 +05:30
zig build test # Run all tests
zig build bench # Run benchmarks
2026-01-22 11:08:52 +05:30
```
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