Skip to content

SSR + compression

Takes the React SSR example one step further: after rendering HTML, compress it with Brotli. This tests whether it’s better to compress on the worker (render + compress in one shot) or on the host (render on worker, compress on main thread). Spoiler: doing both on the worker usually wins because you avoid sending uncompressed HTML back across the thread boundary.

The host generates JSON payload strings. Both paths render the same user card component, then Brotli-compress the HTML output. The benchmark compares compressed byte totals for parity, then measures throughput.

  • bench_react_ssr_compress.ts — the benchmark
  • render_user_card_compressed.tsx — the SSR + compression task
  • utils.ts — shared payload and compression helpers

Input is the same JSON payload shape as the plain React SSR example. The worker call is:

const compressed = await pool.call.renderUserCardCompressed(payloadJson);

Output is a Brotli-compressed Buffer, not an HTML string. That is the point of this example: do the expensive render and compression work in one place, then return the smaller payload.

deno.sh
deno add --npm jsr:@vixeny/knitting
deno add npm:react npm:react-dom npm:mitata

Compression uses built-in node:zlib — no extra packages.

bun.sh
bun src/bench_react_ssr_compress.ts

Expected output:

byte parity check: host=42,380 worker=42,380 OK match
benchmark avg (ns) min ... max (ns)
host 45,600 41,200 ... 58,300
knitting 24,100 21,800 ... 32,400

Brotli is significantly more expensive than renderToString alone, so the worker advantage is more pronounced here than in the plain SSR example.

render_user_card_compressed.tsx
import { task } from "@vixeny/knitting";
import { brotliCompressSync } from "node:zlib";
import { renderUserCardHost } from "../react_ssr/render_user_card.tsx";
function compressHtml(html: string) {
return brotliCompressSync(html);
}
export const renderUserCardCompressed = task({
f: (payload: string) => {
const html = renderUserCardHost(payload);
const compressed = compressHtml(html);
return compressed;
},
});

Where you compress affects both throughput and data transfer. If you compress on the worker, you send a small compressed buffer back to the host. If you compress on the host, you first transfer the full uncompressed HTML string across the thread boundary, then compress it. For response compression in a real server, doing render + compress on the worker is almost always the right call.