diff --git a/benches/client/.gitignore b/benches/client/.gitignore
new file mode 100644
index 00000000..83937100
--- /dev/null
+++ b/benches/client/.gitignore
@@ -0,0 +1,10 @@
+# Zig
+.zig-cache
+# zig-out
+
+# ZX
+/dist
+/bundle
+
+# Node.js
+node_modules
\ No newline at end of file
diff --git a/benches/client/README.md b/benches/client/README.md
new file mode 100644
index 00000000..81b9c834
--- /dev/null
+++ b/benches/client/README.md
@@ -0,0 +1,28 @@
+# ZX
+This website is built with [ZX](https://github.com/nurulhudaapon/zx).
+
+**[Full Documentation →](https://ziex.dev)**
+
+## Installation of ZX
+
+##### Linux/macOS
+```bash
+curl -fsSL https://ziex.dev/install | bash
+```
+
+##### Windows
+```powershell
+powershell -c "irm ziex.dev/install.ps1 | iex"
+
+```
+##### Installing Zig
+```bash
+brew install zig # macOS
+winget install -e --id zig.zig # Windows
+```
+[_See for other platforms →_](https://ziglang.org/learn/getting-started/)
+
+## Running this Project
+```bash
+zx serve
+```
\ No newline at end of file
diff --git a/benches/client/assets/main.wasm b/benches/client/assets/main.wasm
new file mode 100644
index 00000000..239c2fdc
Binary files /dev/null and b/benches/client/assets/main.wasm differ
diff --git a/benches/client/build.zig b/benches/client/build.zig
new file mode 100644
index 00000000..bd2000f8
--- /dev/null
+++ b/benches/client/build.zig
@@ -0,0 +1,32 @@
+const std = @import("std");
+const zx = @import("zx");
+
+pub fn build(b: *std.Build) !void {
+ // --- Target and Optimize from `zig build` arguments ---
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ // --- Root Module ---
+ const mod = b.addModule("root_mod", .{
+ .root_source_file = b.path("src/root.zig"),
+ .target = target,
+ .optimize = optimize,
+ });
+
+ // --- ZX Setup (sets up ZX, dependencies, executables and `serve` step) ---
+ const site_exe = b.addExecutable(.{
+ .name = "zx_site",
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("site/main.zig"),
+ .target = target,
+ .optimize = optimize,
+ .imports = &.{
+ .{ .name = "root_mod", .module = mod },
+ },
+ }),
+ });
+
+ _ = try zx.init(b, site_exe, .{
+ .experimental = .{ .enabled_csr = true },
+ });
+}
diff --git a/benches/client/build.zig.zon b/benches/client/build.zig.zon
new file mode 100644
index 00000000..86a3565e
--- /dev/null
+++ b/benches/client/build.zig.zon
@@ -0,0 +1,17 @@
+.{
+ .name = .zx_site,
+ .version = "0.0.0",
+ .fingerprint = 0xc04151551dc3c31d,
+ .minimum_zig_version = "0.15.2",
+ .dependencies = .{
+ .zx = .{
+ .url = "git+https://github.com/nurulhudaapon/zx#a94b0efab6d9d2c30ccaa22eb80bb8051e76ee34",
+ .hash = "zx-0.1.0-dev.562-8okzKu7fgQAQaD7ebNtUdcIzmcdxEjtCvO3-GKhrojWv",
+ },
+ },
+ .paths = .{
+ "build.zig",
+ "build.zig.zon",
+ "src",
+ },
+}
diff --git a/benches/client/favicon.ico b/benches/client/favicon.ico
new file mode 100644
index 00000000..db14b3ff
Binary files /dev/null and b/benches/client/favicon.ico differ
diff --git a/benches/client/index.html b/benches/client/index.html
new file mode 100644
index 00000000..1cbd6e21
--- /dev/null
+++ b/benches/client/index.html
@@ -0,0 +1,8 @@
+
+
ZXZX Client-Side keyed
+
+
\ No newline at end of file
diff --git a/benches/client/package-lock.json b/benches/client/package-lock.json
new file mode 100644
index 00000000..0a423d3c
--- /dev/null
+++ b/benches/client/package-lock.json
@@ -0,0 +1,13 @@
+{
+ "name": "js-framework-benchmark-zx",
+ "version": "1.1.1",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "js-framework-benchmark-zx",
+ "version": "1.1.1",
+ "license": "Apache-2.0"
+ }
+ }
+}
diff --git a/benches/client/package.json b/benches/client/package.json
new file mode 100644
index 00000000..65923f4a
--- /dev/null
+++ b/benches/client/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "js-framework-benchmark-zx",
+ "version": "1.1.1",
+ "description": "Vanilla.JS demo",
+ "main": "index.js",
+ "js-framework-benchmark": {
+ "frameworkVersion": "",
+ "issues": [
+ 772
+ ]
+ },
+ "scripts": {
+ "dev": "exit 0",
+ "build-prod": "exit 0"
+ },
+ "keywords": [
+ "react",
+ "webpack"
+ ],
+ "author": "Stefan Krause",
+ "license": "Apache-2.0",
+ "homepage": "https://github.com/krausest/js-framework-benchmark",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/krausest/js-framework-benchmark.git"
+ }
+}
diff --git a/benches/client/site/main.zig b/benches/client/site/main.zig
new file mode 100644
index 00000000..09f1b63b
--- /dev/null
+++ b/benches/client/site/main.zig
@@ -0,0 +1,34 @@
+const meta = @import("zx_meta").meta;
+const std = @import("std");
+const zx = @import("zx");
+const builtin = @import("builtin");
+
+const config = zx.App.Config{ .server = .{}, .meta = meta };
+
+pub fn main() !void {
+ if (zx.platform == .browser) return;
+
+ var gpa = std.heap.GeneralPurposeAllocator(.{}){};
+ const allocator = gpa.allocator();
+ defer _ = gpa.deinit();
+
+ const app = try zx.App.init(allocator, config);
+ defer app.deinit();
+
+ app.info();
+ try app.start();
+}
+
+var client = zx.Client.init(
+ zx.client_allocator,
+ .{ .components = &@import("zx_components").components },
+);
+
+export fn mainClient() void {
+ if (zx.platform != .browser) return;
+
+ client.info();
+ client.renderAll();
+}
+
+pub const std_options = zx.std_options;
diff --git a/benches/client/site/pages/layout.zx b/benches/client/site/pages/layout.zx
new file mode 100644
index 00000000..b8efe4df
--- /dev/null
+++ b/benches/client/site/pages/layout.zx
@@ -0,0 +1,17 @@
+pub fn Layout(ctx: zx.LayoutContext, children: zx.Component) zx.Component {
+ return (
+
+
+ ZX
+
+
+
+
+ {children}
+
+
+
+ );
+}
+
+const zx = @import("zx");
diff --git a/benches/client/site/pages/page.zx b/benches/client/site/pages/page.zx
new file mode 100644
index 00000000..6b724a90
--- /dev/null
+++ b/benches/client/site/pages/page.zx
@@ -0,0 +1,174 @@
+
+pub fn Page(ctx: zx.PageContext) zx.Component {
+ return (
+
+
+
+ );
+}
+
+const Item = struct { id: u32, text: []const u8 };
+
+pub fn SimpleApp(allocator: zx.Allocator) zx.Component {
+
+ return (
+
+
+
+
+
{"ZX Client-Side keyed"}
+
+
+
+
+
+
+
+ {for (items.items) |item| (
+
+ | {item.id} |
+
+ {item.text}
+ |
+
+
+
+
+ |
+ |
+
+ )}
+
+
+
+
+ );
+}
+
+const ButtonProps = struct { onclick: *const fn (zx.EventContext) void, title: []const u8, id: []const u8 };
+
+fn Button(ctx: *zx.ComponentCtx(ButtonProps)) zx.Component {
+ return (
+
+
+
+ );
+}
+var next_id: u32 = 1;
+var items = std.ArrayList(Item).empty;
+
+
+
+fn clearRows(_: zx.EventContext) void {
+ items.clearRetainingCapacity();
+ next_id = 1;
+ zx.requestRender();
+}
+
+fn swapRows(_: zx.EventContext) void {
+ if (items.items.len > 998) {
+ const tmp = items.items[1];
+ items.items[1] = items.items[998];
+ items.items[998] = tmp;
+ zx.requestRender();
+ }
+}
+
+fn updateRows(_: zx.EventContext) void {
+ var i: usize = 0;
+ while (i < items.items.len) : (i += 10) {
+ const item = &items.items[i];
+ const new_text = std.fmt.allocPrint(zx.client_allocator, "{s} !!!", .{item.text}) catch @panic("OOM");
+ item.text = new_text;
+ }
+ zx.requestRender();
+}
+
+var seed: u32 = 12345;
+fn RandomNumber(max: u32) u32 {
+ seed = seed *% 1103515245 +% 12345;
+ return seed % max;
+}
+
+
+const adjectives = [_][]const u8{
+ "pretty",
+ "large",
+ "big",
+ "small",
+ "tall",
+ "short",
+ "long",
+ "handsome",
+ "plain",
+ "quaint",
+ "clean",
+ "elegant",
+ "easy",
+ "angry",
+ "crazy",
+ "helpful",
+ "mushy",
+ "odd",
+ "unsightly",
+ "adorable",
+ "important",
+ "inexpensive",
+ "cheap",
+ "expensive",
+ "fancy",
+ };
+const colours = [_][]const u8{ "red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black", "orange" };
+const nouns = [_][]const u8{ "table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger", "pizza", "mouse", "keyboard" };
+
+
+fn BuildRows(count_to_add: u32, clear_first: bool) void {
+
+ std.log.info("BuildRows called with count_to_add: {d}", .{count_to_add});
+ if (clear_first) {
+ items.clearRetainingCapacity();
+ next_id = 1;
+ }
+
+
+ for (0..count_to_add) |_| {
+ const text = std.fmt.allocPrint(zx.client_allocator, "{s} {s} {s}", .{ adjectives[RandomNumber(adjectives.len)], colours[RandomNumber(colours.len)], nouns[RandomNumber(nouns.len)] }) catch @panic("OOM");
+ const item = Item{
+ .id = next_id,
+ .text = text,
+ };
+
+ items.append(zx.client_allocator, item) catch @panic("OOM");
+ next_id += 1;
+ }
+
+ std.log.info("Total items after BuildRows: {d}", .{items.items.len});
+ zx.requestRender();
+ std.log.info("after: {d}", .{items.items.len});
+ // count.set(@intCast(items.items.len));
+}
+
+fn create_rows(_: zx.EventContext) void {
+ BuildRows(1000, true);
+}
+fn create_large_rows(_: zx.EventContext) void {
+ BuildRows(10000, true);
+}
+
+fn append_rows(_: zx.EventContext) void {
+ BuildRows(1000, false);
+}
+
+const zx = @import("zx");
+const std = @import("std");
diff --git a/benches/client/site/public/favicon.ico b/benches/client/site/public/favicon.ico
new file mode 100644
index 00000000..db14b3ff
Binary files /dev/null and b/benches/client/site/public/favicon.ico differ
diff --git a/benches/client/src/root.zig b/benches/client/src/root.zig
new file mode 100644
index 00000000..c855384f
--- /dev/null
+++ b/benches/client/src/root.zig
@@ -0,0 +1,3 @@
+pub fn get_about_zx() []const u8 {
+ return "ZX is a framework for building web applications with Zig.";
+}