const features = [_]Feature{
.{ .icon_fn = icons.Lightning, .title = "Fast Performance", .description = "Significantly faster at SSR than many other frameworks. Optimized for speed and low latency." },
.{ .icon_fn = icons.Code, .title = "Familiar Syntax", .description = "Familiar plain HTML-style markup, with full access to Zig's control flow." },
.{ .icon_fn = icons.Folder, .title = "File System Routing", .description = "Just files in folders to create routes." },
.{ .icon_fn = icons.Server, .title = "API Routes", .description = "Create API endpoints by adding route.zig files to your project." },
.{ .icon_fn = icons.Monitor, .title = "Hybrid Rendering", .description = "Client-side rendering for interactive experiences when you need it." },
.{ .icon_fn = icons.Flow, .title = "Control Flow", .description = "if/else, for/while, and switch all work as expected. It's just Zig syntax." },
.{ .icon_fn = icons.Wrench, .title = "Developer Tooling", .description = "CLI, hot reload, and editor extensions for the best development experience." },
.{ .icon_fn = icons.Rocket, .title = "Deploy Anywhere", .description = "Standalone binaries, Cloudflare, Vercel, or any edge runtime host. Build once, run anywhere." },
.{ .icon_fn = icons.Layers, .title = "Predictable Memory", .description = "Explicit memory management of Zig, no hidden allocations." },
};
const deployments = [_]Deployment{
.{ .icon_fn = icons.Terminal, .title = "Standalone", .description = "Deploy as a self-contained binary on Linux, macOS, Windows andy many more platforms that Zig supports." },
.{ .icon_fn = icons.Globe, .title = "Edge Anywhere", .description = "Deploy to the edge via WASI. Native bindings for Cloudflare and Vercel." },
.{ .icon_fn = icons.Layers, .title = "Static", .description = "Generate a statically pre-rendered site at build time. Deploy to GitHub Pages, Netlify, S3, or any CDN." },
.{ .icon_fn = icons.Package, .title = "Cross-Platform", .description = "Build for any target from any host. Easily cross-compile optimized binaries for x86_64, aarch64, and more." },
};
const benchmark_cwv_rows = [_]BenchmarkRow{
.{ .label = "Ziex", .value = 98, .display = "98%", .url = "" },
.{ .label = "Next.js", .value = 44, .display = "44%", .url = "https://nextjs.org" },
.{ .label = "Nuxt", .value = 42, .display = "42%", .url = "https://nuxt.com" },
.{ .label = "Leptos", .value = 27, .display = "27%", .url = "https://leptos.dev" },
};
const bench_data = @import("bench.zon");
const benchfig = @import("benchfig.zon");
const bench_run = bench_data.run;
const bench_results = bench_data.results;
const bench_len = @typeInfo(@TypeOf(bench_results)).@"struct".field_types.len;
fn getFrameworkUrl(comptime id: []const u8) []const u8 {
if (std.mem.eql(u8, id, "jetzig")) return "https://jetzig.dev";
if (std.mem.eql(u8, id, "leptos")) return "https://leptos.dev";
if (std.mem.eql(u8, id, "solidjs")) return "https://start.solidjs.com";
if (std.mem.eql(u8, id, "dioxus")) return "https://dioxuslabs.com";
if (std.mem.eql(u8, id, "nextjs")) return "https://nextjs.org";
return "";
}
fn sortRows(rows: []BenchmarkRow, comptime lower_is_better: bool) void {
std.sort.insertion(BenchmarkRow, rows, {}, struct {
fn lessThan(_: void, a: BenchmarkRow, b: BenchmarkRow) bool {
return if (lower_is_better) a.value < b.value else a.value > b.value;
}
}.lessThan);
}
fn buildSsrRows() [bench_len]BenchmarkRow {
@setEvalBranchQuota(10000);
var rows: [bench_len]BenchmarkRow = undefined;
const field_names = @typeInfo(@TypeOf(bench_results)).@"struct".field_names;
inline for (field_names, 0..) |field_name, i| {
const d = @field(bench_results, field_name);
rows[i] = .{
.label = d.label,
.value = d.requests_per_sec,
.display = util.formatLargeNumber(d.requests_per_sec),
.url = getFrameworkUrl(d.id),
.tooltip_metrics = &[_]TooltipMetric{
.{ .label = "Framework", .value = d.label },
.{ .label = "Requests/sec", .value = util.formatRequests(d.requests_per_sec) },
.{ .label = "P50 Latency", .value = util.formatLatency(d.p50_latency_ms) },
.{ .label = "P99 Latency", .value = util.formatLatency(d.p99_latency_ms) },
},
};
}
sortRows(&rows, false);
return rows;
}
fn buildMemoryRows() [bench_len]BenchmarkRow {
@setEvalBranchQuota(10000);
var rows: [bench_len]BenchmarkRow = undefined;
const field_names = @typeInfo(@TypeOf(bench_results)).@"struct".field_names;
inline for (field_names, 0..) |field_name, i| {
const d = @field(bench_results, field_name);
rows[i] = .{
.label = d.label,
.value = d.peak_memory_mb,
.display = util.formatMemory(d.peak_memory_mb),
.url = getFrameworkUrl(d.id),
.tooltip_metrics = &[_]TooltipMetric{
.{ .label = "Framework", .value = d.label },
.{ .label = "Idle Memory", .value = util.formatMemory(d.idle_memory_mb) },
.{ .label = "Peak Memory", .value = util.formatMemory(d.peak_memory_mb) },
},
};
}
sortRows(&rows, true);
return rows;
}
fn buildBuildTimeRows() [bench_len]BenchmarkRow {
@setEvalBranchQuota(10000);
var rows: [bench_len]BenchmarkRow = undefined;
const field_names = @typeInfo(@TypeOf(bench_results)).@"struct".field_names;
inline for (field_names, 0..) |field_name, i| {
const d = @field(bench_results, field_name);
rows[i] = .{
.label = d.label,
.value = d.build_time_s,
.display = util.formatBuildTime(d.build_time_s),
.url = getFrameworkUrl(d.id),
.tooltip_metrics = &[_]TooltipMetric{
.{ .label = "Framework", .value = d.label },
.{ .label = "Build Time", .value = util.formatBuildTime(d.build_time_s) },
},
};
}
sortRows(&rows, true);
return rows;
}
fn buildColdStartRows() [bench_len]BenchmarkRow {
@setEvalBranchQuota(10000);
var rows: [bench_len]BenchmarkRow = undefined;
const field_names = @typeInfo(@TypeOf(bench_results)).@"struct".field_names;
inline for (field_names, 0..) |field_name, i| {
const d = @field(bench_results, field_name);
rows[i] = .{
.label = d.label,
.value = d.cold_start_ms,
.display = util.formatColdStart(d.cold_start_ms),
.url = getFrameworkUrl(d.id),
.tooltip_metrics = &[_]TooltipMetric{
.{ .label = "Framework", .value = d.label },
.{ .label = "Cold Start", .value = util.formatColdStart(d.cold_start_ms) },
.{ .label = "Image Size", .value = util.formatImageSize(d.image_mb) },
},
};
}
sortRows(&rows, true);
return rows;
}
fn buildImageSizeRows() [bench_len]BenchmarkRow {
@setEvalBranchQuota(10000);
var rows: [bench_len]BenchmarkRow = undefined;
const field_names = @typeInfo(@TypeOf(bench_results)).@"struct".field_names;
inline for (field_names, 0..) |field_name, i| {
const d = @field(bench_results, field_name);
rows[i] = .{
.label = d.label,
.value = d.image_mb,
.display = util.formatImageSize(d.image_mb),
.url = getFrameworkUrl(d.id),
.tooltip_metrics = &[_]TooltipMetric{
.{ .label = "Framework", .value = d.label },
.{ .label = "Image Size", .value = util.formatImageSize(d.image_mb) },
.{ .label = "Binary Size", .value = util.formatBinarySize(d.binary_mb) },
},
};
}
sortRows(&rows, true);
return rows;
}
fn buildCpuUsageRows() [bench_len]BenchmarkRow {
@setEvalBranchQuota(10000);
var rows: [bench_len]BenchmarkRow = undefined;
const field_names = @typeInfo(@TypeOf(bench_results)).@"struct".field_names;
inline for (field_names, 0..) |field_name, i| {
const d = @field(bench_results, field_name);
rows[i] = .{
.label = d.label,
.value = d.cpu_avg_pct,
.display = util.formatCpuPeak(d.cpu_avg_pct),
.url = getFrameworkUrl(d.id),
.tooltip_metrics = &[_]TooltipMetric{
.{ .label = "Framework", .value = d.label },
.{ .label = "Avg CPU", .value = util.formatCpuPeak(d.cpu_avg_pct) },
.{ .label = "Peak CPU", .value = util.formatCpuPeak(d.cpu_peak_pct) },
.{ .label = "Requests/sec", .value = util.formatRequests(d.requests_per_sec) },
},
};
}
sortRows(&rows, true);
return rows;
}
const benchmark_ssr_rows = buildSsrRows();
const benchmark_memory_rows = buildMemoryRows();
const benchmark_build_time_rows = buildBuildTimeRows();
const benchmark_cold_start_rows = buildColdStartRows();
const benchmark_image_size_rows = buildImageSizeRows();
const benchmark_cpu_usage_rows = buildCpuUsageRows();
const benchmark_tabs = [_]BenchmarkTab{
// .{
// .id = "bench-tab-cwv",
// .label = "Core Web Vitals",
// .metric = "% of real-world sites with good Core Web Vitals",
// .footnote = "Full report. Benchmarked against Next.js and Leptos using real-world Core Web Vitals.",
// .rows = benchmark_cwv_rows[0..],
// .checked = true,
// },
.{
.id = "bench-tab-ssr",
.label = "SSR",
.metric = "Requests per second (higher is better)",
.footnote = std.fmt.comptimePrint(
"Dockerized SSR endpoint hit with {d} requests at {d} concurrent connections, averaged over {d} runs.",
.{
benchfig.requests,
benchfig.concurrency,
benchfig.runs,
},
),
.rows = benchmark_ssr_rows[0..],
.checked = true,
},
.{
.id = "bench-tab-cpu",
.label = "CPU",
.metric = "Average CPU usage during load (lower is better)",
.footnote = "Average CPU utilization during the load test via cgroup cpu.stat. Hover for peak CPU.",
.rows = benchmark_cpu_usage_rows[0..],
.checked = false,
.lower_is_better = true,
},
.{
.id = "bench-tab-memory",
.label = "Memory",
.metric = "Peak memory under load (lower is better)",
.footnote = "Peak container RSS during load test. Lower is better. Hover for idle memory.",
.rows = benchmark_memory_rows[0..],
.checked = false,
.lower_is_better = true,
},
.{
.id = "bench-tab-cold-start",
.label = "Cold Start",
.metric = "Cold start time in milliseconds (lower is better)",
.footnote = "Time for the container to start and become ready to serve requests. Lower is better.",
.rows = benchmark_cold_start_rows[0..],
.checked = false,
.lower_is_better = true,
},
.{
.id = "bench-tab-image-size",
.label = "Size",
.metric = "Docker image size (lower is better)",
.footnote = "Size of the final Docker image. Smaller images are faster to deploy and use less storage.",
.rows = benchmark_image_size_rows[0..],
.checked = false,
.lower_is_better = true,
},
// TODO: currently it is hard to auto calc build time from inside docker build
// .{
// .id = "bench-tab-build",
// .label = "Build Time",
// .metric = "Docker image build time in seconds (lower is better)",
// .footnote = "Time to build each framework's Docker image from scratch on the host machine. Lower is better.",
// .rows = benchmark_build_time_rows[0..],
// .checked = false,
// .lower_is_better = true,
// },
};
pub fn Page(ctx: zx.PageContext) zx.Component {
const allocator = ctx.arena;
const current_year = blk: {
const timestamp: i64 = @intCast(@divFloor(std.Io.Timestamp.now(zx.io(), .real).nanoseconds, std.time.ns_per_s));
const seconds_per_year = 365.25 * 24 * 60 * 60;
const years_since_epoch = @as(i64, @intFromFloat(@as(f64, @floatFromInt(timestamp)) / seconds_per_year));
break :blk 1970 + years_since_epoch;
};
const feature_examples = @embedFile("./examples/feature_examples.zx");
const control_tabs = [_]CodeTab{
.{ .id = "control-tab-if", .label = "If", .section = "Control Flow: if", .code = "", .checked = true },
.{ .id = "control-tab-if-opt", .label = "If Optional", .section = "Control Flow: if optional capture", .code = "", .checked = false },
.{ .id = "control-tab-if-err", .label = "If Error", .section = "Control Flow: if error capture", .code = "", .checked = false },
.{ .id = "control-tab-while", .label = "While", .section = "Control Flow: while", .code = "", .checked = false },
.{ .id = "control-tab-while-opt", .label = "While Optional", .section = "Control Flow: while optional capture", .code = "", .checked = false },
.{ .id = "control-tab-while-err", .label = "While Error", .section = "Control Flow: while error capture", .code = "", .checked = false },
};
const caching_tabs = [_]CodeTab{
.{ .id = "caching-tab-component", .label = "Component", .section = "Caching: component", .code = "", .checked = true },
.{ .id = "caching-tab-page", .label = "Page", .section = "Caching: page", .code = "", .checked = false },
};
const fs_tabs = [_]CodeTab{
.{ .id = "fs-tab-page", .label = "Page", .section = "File System Routing: page", .code = "", .checked = true },
.{ .id = "fs-tab-layout", .label = "Layout", .section = "File System Routing: layout", .code = "", .checked = false },
};
const component_tabs = [_]CodeTab{
.{ .id = "component-tab-basics", .label = "Basics", .section = "Component: basics", .code = "", .checked = true },
.{ .id = "component-tab-frag", .label = "Fragment", .section = "Component: fragment", .code = "", .checked = false },
.{ .id = "component-tab-props", .label = "Props", .section = "Component: props", .code = "", .checked = false },
.{ .id = "component-tab-attr", .label = "Attributes", .section = "Component: attr", .code = "", .checked = false },
};
return (
Full-stack web framework for Zig.
Compile-time safety, deterministic performance, absolute simplicity, and a delightful developer experience.
View benchmark → Each framework runs in a Docker container limited to 2 CPU cores and 2 GB RAM.` tabs={benchmark_tabs[0..]} run_url={bench_run.url} run_date={bench_run.date} />
)} />
)} />
)} />
)} />
)} />
)} />
)} />
)} />)} />
);
}
const Deployment = struct { icon_fn: *const fn (zx.Allocator) zx.Component, title: []const u8, description: []const u8 };
const Feature = struct { icon_fn: *const fn (zx.Allocator) zx.Component, title: []const u8, description: []const u8 };
fn FeatureCard(allocator: zx.Allocator, props: Feature) zx.Component {
const IconComponent = props.icon_fn(allocator);
return (
{IconComponent}
{props.title}
{props.description}
);
}
const TooltipMetric = struct {
label: []const u8,
value: []const u8,
};
const BenchmarkRow = struct {
label: []const u8,
value: f64,
display: []const u8,
url: []const u8,
tooltip_metrics: []const TooltipMetric = &[_]TooltipMetric{},
};
const BenchmarkRowViewProps = struct {
row: BenchmarkRow,
normalized_value: f64,
};
const BenchmarkTab = struct {
id: []const u8,
label: []const u8,
metric: []const u8,
footnote: []const u8,
rows: []const BenchmarkRow,
checked: bool,
lower_is_better: bool = false,
};
const BenchmarksSectionProps = struct {
eyebrow: []const u8,
title: []const u8,
subtitle: []const u8,
tabs: []const BenchmarkTab,
run_url: []const u8 = "",
run_date: []const u8 = "",
};
fn buildBenchTabStyles(allocator: zx.Allocator, container_id: []const u8, tabs: []const BenchmarkTab) []const u8 {
var out = std.array_list.Managed(u8).init(allocator);
defer out.deinit();
for (tabs) |tab| {
out.appendSlice("#") catch unreachable;
out.appendSlice(container_id) catch unreachable;
out.appendSlice(":has(#") catch unreachable;
out.appendSlice(tab.id) catch unreachable;
out.appendSlice(":checked) .benchmarks-panel[data-tab=\"") catch unreachable;
out.appendSlice(tab.id) catch unreachable;
out.appendSlice("\"] { display: block; }\n") catch unreachable;
out.appendSlice("#") catch unreachable;
out.appendSlice(container_id) catch unreachable;
out.appendSlice(":has(#") catch unreachable;
out.appendSlice(tab.id) catch unreachable;
out.appendSlice(":checked) label[for=\"") catch unreachable;
out.appendSlice(tab.id) catch unreachable;
out.appendSlice("\"] { color: #0c0f14; background: #4ade80; border-color: transparent; box-shadow: 0 8px 20px rgba(74, 222, 128, 0.25); }\n") catch unreachable;
}
return allocator.dupe(u8, out.items) catch unreachable;
}
fn BenchmarkRowView(allocator: zx.Allocator, props: BenchmarkRowViewProps) zx.Component {
const style_value = std.fmt.allocPrint(allocator, "--value: {d:.3};", .{props.normalized_value}) catch unreachable;
return (
);
}
fn normalizeValue(value: f64, rows: []const BenchmarkRow) f64 {
if (rows.len == 0) return 0;
var max_value = rows[0].value;
for (rows[1..]) |row| {
if (row.value > max_value) max_value = row.value;
}
if (max_value == 0) return 0;
return value / max_value;
}
fn BenchmarksPanel(allocator: zx.Allocator, tab: BenchmarkTab) zx.Component {
const panel_id = std.fmt.allocPrint(allocator, "panel-{s}", .{tab.id}) catch unreachable;
const tab_id = std.fmt.allocPrint(allocator, "tab-{s}", .{tab.id}) catch unreachable;
return (
//
{tab.metric}
{for (tab.rows) |row| (
)}
);
}
fn BenchmarksSection(allocator: zx.Allocator, props: BenchmarksSectionProps) zx.Component {
const tab_styles = buildBenchTabStyles(allocator, "benchmarks-tabs", props.tabs);
return (
);
}
const DeploymentSectionProps = struct {};
fn DeploymentSection(allocator: zx.Allocator, _: DeploymentSectionProps) zx.Component {
return (
{for (deployments) |d| (
{d.icon_fn(allocator)}
{d.title}
{d.description}
)}
);
}
const FeatureExampleProps = struct {
title: []const u8,
child: zx.Component,
wide: bool,
};
fn FeatureExample(allocator: zx.Allocator, props: FeatureExampleProps) zx.Component {
const class_name = if (props.wide) "feature-example feature-example--wide" else "feature-example";
return (
{props.title}
{props.child}
);
}
const CodeWindowProps = struct {
title: []const u8,
code: []const u8,
section: []const u8,
source: []const u8,
};
const CodeTab = struct {
id: []const u8,
label: []const u8,
section: []const u8,
code: []const u8,
checked: bool,
};
fn resolveCode(allocator: zx.Allocator, source: []const u8, section: []const u8, fallback: []const u8) []const u8 {
const raw = if (section.len > 0 and source.len > 0)
util.extractSection(allocator, source, section)
else
fallback;
return util.highlightZx(allocator, raw) catch raw;
}
fn CodeWindow(allocator: zx.Allocator, props: CodeWindowProps) zx.Component {
const highlighted = resolveCode(allocator, props.source, props.section, props.code);
return (
);
}
const CodeWindowTabsProps = struct {
id: []const u8,
name: []const u8,
title: []const u8,
tabs: []const CodeTab,
video_url: []const u8,
source: []const u8,
};
const CodePreviewExampleProps = struct {
title: []const u8,
section: []const u8,
source: []const u8,
code: []const u8,
preview: zx.Component,
};
fn CodePreviewExample(allocator: zx.Allocator, props: CodePreviewExampleProps) zx.Component {
const highlighted = resolveCode(allocator, props.source, props.section, props.code);
return (
);
}
fn buildTabStyles(allocator: zx.Allocator, container_id: []const u8, tabs: []const CodeTab) ![]const u8 {
var out = std.array_list.Managed(u8).init(allocator);
defer out.deinit();
for (tabs) |tab| {
try out.appendSlice("#");
try out.appendSlice(container_id);
try out.appendSlice(":has(#");
try out.appendSlice(tab.id);
try out.appendSlice(":checked) .code-example-panel[data-tab=\"");
try out.appendSlice(tab.id);
try out.appendSlice("\"] { display: block; }\n");
}
return try allocator.dupe(u8, out.items);
}
fn CodeWindowTabs(allocator: zx.Allocator, props: CodeWindowTabsProps) !zx.Component {
const tab_styles = try buildTabStyles(allocator, props.id, props.tabs);
return (
{for (props.tabs) |tab| (renderTabPanel(allocator, props.source, tab))}
);
}
fn renderTabPanel(allocator: zx.Allocator, source: []const u8, tab: CodeTab) zx.Component {
const highlighted = resolveCode(allocator, source, tab.section, tab.code);
return (
);
}
const InstallCLI = @import("./components/install_guide.zx").InstallCLI;
const VersionMeta = @import("./components/version.zx").VersionMeta;
const Examples = @import("./examples/feature_examples.zx");
const icons = @import("./components/icons.zx");
const util = @import("./reference/util.zig");
const root = @import("root");
const std = @import("std");
const zx = @import("zx");