Skip to content

Profiler

Overview

A drop-in SSR profiler for SvelteKit. One line in vite.config.ts. Then open /__profiler to see which components, functions, and network calls make a server render slow, with no per-component instrumentation.

On this page

The profiler answers one question: why is this server render slow? It names the slow components, functions, network calls, and allocations. There is no per-component instrumentation, and nothing to wrap by hand.

You configure it fully in the Vite plugin (ogygia({ profiler: true })). ogygia.handle() mounts it automatically. There is no separate hook to add, and no route files to write.

The idea

When a page takes seconds to render on the server, the cause is rarely visible from the outside. It can be a heavy component, a load function that awaits three services one after another, a markdown pass, or the garbage collector. A CPU profiler alone misses the waits. A request log alone misses the compute.

Thus the profiler watches the whole Node process during a render, and splits the wall clock into everything that occurred:

  • component SSR and load functions,
  • outbound fetch / http calls, tied to the route that made them,
  • garbage collection and memory growth,
  • and time spent in a wait, not in compute.

One trick makes it readable. Svelte compiles each component to a function named after its file. Thus a V8 sample taken inside a render already knows that it is in Cart or ProductGrid. The profiler reads those names directly off the stack. You never wrap a component to measure it.

Install

Set it on in vite.config.ts — nowhere else. ogygia.handle() mounts it for you. Thus src/hooks.server.ts stays a plain sequence(...):

import { ogygia } from 'ogygia/vite';

export default defineConfig({
	plugins: [ogygia({ profiler: true }), sveltekit()]
});

Pass options in place of true to adjust it: ogygia({ profiler: { secret, path, sampleInterval } }). In production, set a secret. Then the UI is reachable and gated:

OGYGIA_PROFILER_SECRET=some-long-random-string

Then open /__profiler. In development, the UI is open. In production, it 404s until you log in with the secret. A session cookie holds the login, so the key never rides in a URL. It is also reachable over MCP: ogygia mcp / ogygia ai give the profiler to an AI agent. The agent can run a profile, or read a downloaded report with no live server.

What you get

  • Always on, near zero cost. Each request is wall-timed and logged, with per-route p50/p95. The CPU is sampled only while you record.
  • On demand. Profile a page by URL, or profile a single request with a header. See Recording.
  • A readable report. A wall-clock budget bar (compute against waits). An interactive treemap of where the CPU went. Sortable component and function tables, with per-call cost and ×N repeat counts. A network waterfall: each request expands to its formatted JSON payload, and its wire and decoded sizes. A waiting-by-function breakdown for non-HTTP I/O. Memory and GC. A flame graph. See Reading a report.
  • Share it, or open it in the devtools. A report exports as an encrypted .ogp. You open it again through Import. That is the serverless answer, where a report cannot stay in memory. If you also set devtools on, its Profiler tab runs the same page profiles inline.

What it needs

A Node.js server: adapter-node, vite preview, or most Node hosts. It uses the built-in V8 inspector. On edge runtimes without the inspector (Cloudflare Workers, Deno Deploy), the always-on request log still operates. CPU and heap recording do not. The UI says so plainly. On serverless Node hosts (Lambda, Amplify), recording operates, but the instance is short-lived. Download the report as a dump and view it anywhere. See Serverless and edge.

It is production-safe. The UI 404s without the secret. The match is timing-safe. Recorded profiles live only in memory, never on disk.