← back to demos

Server-Timing observability

Open Chrome devtools → Network → click this request → "Timing" tab. Each entry below shows up as a colored bar under "Server Timing".

Tip: when a loader returns a raw Response, bext passes it through untouched — Server-Timing, Cache-Control, or any custom headers are preserved as-is. This is the only way to emit Server-Timing from a PRISM route, since standard JSX rendering doesn't give you access to the Response object.

This request

spanduration (ms)
db.query40.0
cache.get15.0
render-prep0.0
total55.0

Verify from curl

curl -sI https://demo.bext.dev/examples/server-timing | grep -i server-timing

Source — src/app/examples/server-timing/page.tsx

// Loader measures a few spans, then returns a raw Response so the
// Server-Timing header is threaded through bext's response pipeline.
// Chrome devtools → Network → this request → "Timing" tab shows each
// span as a labeled colored bar.

export async function loader({ request }: LoaderArgs) {
  const t0 = performance.now();
  await wait(40);              // pretend: db.query
  const t1 = performance.now();
  await wait(15);              // pretend: cache.get
  const t2 = performance.now();
  // ... build body ...
  const t3 = performance.now();
  return new Response(body, {
    status: 200,
    headers: {
      "content-type": "text/html; charset=utf-8",
      "server-timing": [
        `db;dur=${(t1 - t0).toFixed(1)};desc="db.query"`,
        `cache;dur=${(t2 - t1).toFixed(1)};desc="cache.get"`,
        `render;dur=${(t3 - t2).toFixed(1)};desc="render-prep"`,
        `total;dur=${(t3 - t0).toFixed(1)};desc="total"`,
      ].join(", "),
    },
  });
}