Skip to content

Profiler

Overview

An SSR profiler for SvelteKit that you turn on with one line in vite.config.ts. Open /__profiler to see which components, functions and network calls make a server render slow, without instrumenting any component by hand.

On this page

When a page takes seconds to render on the server, the cause is rarely visible from the outside. It might be a heavy component, a load function that awaits three services one after another, a markdown pass, or the garbage collector. A CPU profiler on its own misses the time spent waiting, and a request log on its own misses the time spent computing, so neither tells you the whole story.

The profiler is built to answer that one question, why is this server render slow?, by naming the components, functions, network calls and allocations responsible. You don’t wrap components or add timing code to get there.

How it works

While a render runs, the profiler watches the whole Node process and splits the wall clock into everything that happened during it:

  • 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 rather than in compute.

What makes this readable without instrumentation is that Svelte compiles each component to a function named after its file. A V8 sample taken inside a render therefore already knows it is in Cart or ProductGrid, and the profiler reads those names straight off the stack.

Install

The profiler ships in @ogygia/devtools, so install that package:

pnpm add @ogygia/devtools

You turn it on in vite.config.ts and nowhere else. List the devtools() plugin beside ogygia(); ogygia.handle() mounts the profiler for you, so there is no extra hook to add, no route files to write, and src/hooks.server.ts stays a plain sequence(...):

import { ogygia } from 'ogygia/vite';
import { devtools } from '@ogygia/devtools/vite';

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

This also turns on the devtools dock. If you want the profiler without the dock, write devtools({ dock: false, profiler: true }), and if you want to adjust it, pass options in place of true: devtools({ profiler: { secret, path, sampleInterval } }). The full list is under Options.

Then open /__profiler. In development the UI is open, while in production it answers 404 until you set a secret and log in with it:

OGYGIA_PROFILER_SECRET=some-long-random-string

In production covers the login in more detail. The profiler is also reachable over MCP: ogygia mcp / ogygia ai give it to an AI agent, which can run a profile or read a downloaded report without a live server.

What you get

  • Request timing that is always on and costs almost nothing. Each request is wall-timed and logged with per-route p50/p95, and the CPU is sampled only while you record.
  • Profiles on demand. You can profile a page by URL or profile a single request with a header, as described in Recording.
  • A report you can read. It has a wall-clock budget bar that separates compute from waits, an interactive treemap of where the CPU went, sortable component and function tables with per-call cost and ×N repeat counts, and a network waterfall in which each request expands to its formatted JSON payload with its wire and decoded sizes. There is also a waiting-by-function breakdown for non-HTTP I/O, a memory and GC section, and a flame graph. Reading a report walks through each part.
  • Reports you can share or open in the devtools. A report exports as an encrypted .ogp file that you open again through Import, which is how you keep a report on a serverless host where it cannot stay in memory. With devtools on, its Profiler tab runs the same page profiles inline.

What it needs

The profiler needs a Node.js server (adapter-node, vite preview, or most Node hosts) because it uses the built-in V8 inspector. On edge runtimes that have no inspector, such as Cloudflare Workers and Deno Deploy, the always-on request log still works but CPU and heap recording do not, and the UI tells you so. On serverless Node hosts such as Lambda and Amplify, recording works but the instance is short-lived, so you either download the report as a dump and view it elsewhere, or give every instance a shared store; see Serverless and edge.

It is safe to leave on in production: the UI answers 404 without the secret, and the secret is compared in constant time. Recorded profiles stay in the server’s memory unless you give the profiler a store yourself, such as a SQLite file (.ogygia/profiles.db by default), Redis or Postgres.