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
loadfunctions, - outbound
fetch/httpcalls, 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-stringThen 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
×Nrepeat 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.