Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 51 additions & 1 deletion examples/benchmark/bench.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ const saveLabel = args.includes("--save")
const compareFile = args.includes("--compare")
? args[args.indexOf("--compare") + 1]
: null;
const filterArg = args.includes("--filter")
? args[args.indexOf("--filter") + 1]
: null;
const filters = filterArg
? filterArg
.split(",")
.map((s) => s.trim())
.filter(Boolean)
: null;

function parseCluster() {
const idx = args.findIndex((a) => a.startsWith("--cluster"));
Expand Down Expand Up @@ -161,7 +170,48 @@ const BENCHMARKS = [
desc: "Static file (JS bundle)",
},
{ name: "404-miss", path: "/nonexistent", desc: "404 miss → SSR" },
];
{
name: "hybrid-min",
path: "/hybrid",
desc: "Hybrid server+6 client siblings (min)",
},
{ name: "hybrid-small", path: "/hybrid/small", desc: "Hybrid small" },
{ name: "hybrid-medium", path: "/hybrid/medium", desc: "Hybrid medium" },
{ name: "hybrid-large", path: "/hybrid/large", desc: "Hybrid large" },
{ name: "hybrid-deep", path: "/hybrid/deep", desc: "Hybrid deep" },
{ name: "hybrid-wide", path: "/hybrid/wide", desc: "Hybrid wide" },
{ name: "hybrid-cached", path: "/hybrid/cached", desc: "Hybrid cached" },
{
name: "hybrid-client-min",
path: "/hybrid/client",
desc: "Hybrid client minimal",
},
{
name: "hybrid-client-small",
path: "/hybrid/client/small",
desc: "Hybrid client small",
},
{
name: "hybrid-client-medium",
path: "/hybrid/client/medium",
desc: "Hybrid client medium",
},
{
name: "hybrid-client-large",
path: "/hybrid/client/large",
desc: "Hybrid client large",
},
{
name: "hybrid-client-deep",
path: "/hybrid/client/deep",
desc: "Hybrid client deep",
},
{
name: "hybrid-client-wide",
path: "/hybrid/client/wide",
desc: "Hybrid client wide",
},
].filter((b) => !filters || filters.some((f) => b.name.includes(f)));

// ── Find an actual JS bundle path ───────────────────────────────────────────

Expand Down
3 changes: 3 additions & 0 deletions examples/benchmark/pages/(hybrid)/(hybrid).layout.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Layout({ children }) {
return children;
}
3 changes: 3 additions & 0 deletions examples/benchmark/pages/(rsc)/(rsc).layout.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Layout({ children }) {
return children;
}
42 changes: 42 additions & 0 deletions examples/benchmark/pages/(rsc)/cached.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Cached page — same content as /medium but with response caching enabled.
* Measures the response cache fast path vs uncached rendering.
*/
import { useResponseCache } from "@lazarv/react-server";

function ProductCard({ id }) {
return (
<article>
<div style={{ background: "#f0f0f0", height: 200, width: "100%" }} />
<h3>Product {id}</h3>
<p>
High-quality item with excellent features. Perfect for everyday use.
Rating: {(id % 5) + 1}/5 stars.
</p>
<div>
<span>${((id * 17 + 29) % 200) + 9.99}</span>
{id % 3 === 0 && <span> (On Sale)</span>}
</div>
<button>Add to Cart</button>
</article>
);
}

export default function Cached() {
useResponseCache(60000);

const products = Array.from({ length: 50 }, (_, i) => i + 1);
return (
<main>
<header>
<h1>Products (Cached)</h1>
<p>Showing {products.length} items</p>
</header>
<div>
{products.map((id) => (
<ProductCard key={id} id={id} />
))}
</div>
</main>
);
}
25 changes: 25 additions & 0 deletions examples/benchmark/pages/(rsc)/deep.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Deep nesting page — 100 levels of component nesting.
* Tests React reconciler / RSC serialization overhead with deep trees.
* Small HTML output but deep virtual DOM.
*/

function Wrapper({ depth, children }) {
if (depth <= 0) return <div className="leaf">{children}</div>;
return (
<div className={`depth-${depth}`}>
<Wrapper depth={depth - 1}>{children}</Wrapper>
</div>
);
}

export default function Deep() {
return (
<main>
<h1>Deep Nesting (100 levels)</h1>
<Wrapper depth={100}>
<p>Leaf node at the bottom of the tree.</p>
</Wrapper>
</main>
);
}
7 changes: 7 additions & 0 deletions examples/benchmark/pages/(rsc)/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* Index page — minimal SSR, no data, tiny HTML.
* Baseline for measuring framework overhead.
*/
export default function Index() {
return <h1>Benchmark</h1>;
}
87 changes: 87 additions & 0 deletions examples/benchmark/pages/(rsc)/large.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* Large page — a data table with 500 rows × 8 columns.
* ~4000+ elements, ~80KB+ HTML. Typical admin dashboard / report.
*/

const COLUMNS = [
"ID",
"Name",
"Email",
"Department",
"Role",
"Status",
"Joined",
"Score",
];

const DEPARTMENTS = [
"Engineering",
"Marketing",
"Sales",
"Support",
"Design",
"Product",
"Finance",
"Legal",
];
const ROLES = [
"Manager",
"Senior",
"Junior",
"Lead",
"Director",
"Intern",
"Principal",
"Staff",
];
const STATUSES = ["Active", "Inactive", "On Leave", "Probation"];

function TableRow({ i }) {
const dept = DEPARTMENTS[i % DEPARTMENTS.length];
const role = ROLES[i % ROLES.length];
const status = STATUSES[i % STATUSES.length];
const score = ((i * 7 + 13) % 100) + 1;
return (
<tr>
<td>{i}</td>
<td>
User {i} {dept[0]}
</td>
<td>user{i}@example.com</td>
<td>{dept}</td>
<td>{role}</td>
<td>{status}</td>
<td>
2024-{String((i % 12) + 1).padStart(2, "0")}-
{String((i % 28) + 1).padStart(2, "0")}
</td>
<td>{score}</td>
</tr>
);
}

export default function Large() {
const rows = Array.from({ length: 500 }, (_, i) => i + 1);
return (
<main>
<header>
<h1>Employee Directory</h1>
<p>{rows.length} records</p>
</header>
<table>
<thead>
<tr>
{COLUMNS.map((col) => (
<th key={col}>{col}</th>
))}
</tr>
</thead>
<tbody>
{rows.map((i) => (
<TableRow key={i} i={i} />
))}
</tbody>
</table>
</main>
);
}
39 changes: 39 additions & 0 deletions examples/benchmark/pages/(rsc)/medium.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Medium page — a product listing with 50 items.
* ~200 elements, ~10KB HTML. Typical e-commerce or dashboard page.
*/

function ProductCard({ id }) {
return (
<article>
<div style={{ background: "#f0f0f0", height: 200, width: "100%" }} />
<h3>Product {id}</h3>
<p>
High-quality item with excellent features. Perfect for everyday use.
Rating: {(id % 5) + 1}/5 stars.
</p>
<div>
<span>${((id * 17 + 29) % 200) + 9.99}</span>
{id % 3 === 0 && <span> (On Sale)</span>}
</div>
<button>Add to Cart</button>
</article>
);
}

export default function Medium() {
const products = Array.from({ length: 50 }, (_, i) => i + 1);
return (
<main>
<header>
<h1>Products</h1>
<p>Showing {products.length} items</p>
</header>
<div>
{products.map((id) => (
<ProductCard key={id} id={id} />
))}
</div>
</main>
);
}
31 changes: 31 additions & 0 deletions examples/benchmark/pages/(rsc)/small.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Small page — a handful of elements, typical for a landing page hero section.
* ~20 elements, ~1KB HTML.
*/
export default function Small() {
return (
<main>
<header>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
</header>
<section>
<h1>Welcome to Our Platform</h1>
<p>
Build modern web applications with server components. Fast, reliable,
and easy to use.
</p>
<div>
<button>Get Started</button>
<button>Learn More</button>
</div>
</section>
<footer>
<p>&copy; 2026 Benchmark App</p>
</footer>
</main>
);
}
27 changes: 27 additions & 0 deletions examples/benchmark/pages/(rsc)/wide.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Wide page — 1000 sibling components.
* Tests RSC serialization and HTML rendering with broad, flat trees.
* ~3000 elements, ~30KB HTML.
*/

function Item({ i }) {
return (
<li>
<span>Item #{i}</span> — <em>{i % 2 === 0 ? "even" : "odd"}</em>
</li>
);
}

export default function Wide() {
const items = Array.from({ length: 1000 }, (_, i) => i + 1);
return (
<main>
<h1>Wide Tree (1000 siblings)</h1>
<ul>
{items.map((i) => (
<Item key={i} i={i} />
))}
</ul>
</main>
);
}
3 changes: 3 additions & 0 deletions examples/benchmark/pages/(ssr)/(ssr).layout.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Layout({ children }) {
return children;
}
26 changes: 26 additions & 0 deletions examples/benchmark/pages/(ssr)/client/deep.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"use client";

/**
* Deep nesting page — client component SSR path.
* Same as /deep but rendered as a client component.
*/

function Wrapper({ depth, children }) {
if (depth <= 0) return <div className="leaf">{children}</div>;
return (
<div className={`depth-${depth}`}>
<Wrapper depth={depth - 1}>{children}</Wrapper>
</div>
);
}

export default function Deep() {
return (
<main>
<h1>Deep Nesting (100 levels)</h1>
<Wrapper depth={100}>
<p>Leaf node at the bottom of the tree.</p>
</Wrapper>
</main>
);
}
9 changes: 9 additions & 0 deletions examples/benchmark/pages/(ssr)/client/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"use client";

/**
* Index page — minimal SSR via client component path.
* Same as / but rendered as a client component (no RSC Flight serialization).
*/
export default function Index() {
return <h1>Benchmark</h1>;
}
Loading
Loading