Skip to content

Reference

Performance

Client JS, Lighthouse, CLS/LCP, and JS eval measured against SvelteKit, Astro and Mochi on identical pages.

On this page

ogygia is an islands library for SvelteKit. Here are the measurements, reproducible in-repo under bench/.

The benchmark

The same blog post (same prose, same five interactive widgets, same CSS) is built four ways, and we measure how much client JavaScript each ships as the post gets longer. Islands ship JS only for the widgets, so their payload is flat. Whole-page hydration ships JS for the entire document, so it grows with the content.

The method is a port of Stanislav Khromov’s interactive-blogs-benchmark, with full credit to @khromov. We run it on latest tooling with ogygia added.

Client JavaScript as the post grows longer — islands stay flat; whole-page hydration climbs.
0125250375500JS KB (uncompressed)9k23k71kwords in postogygiasveltekitastromochi
Client JS (brotli)
ogygia
25 KB
sveltekit
34 KB
astro
6 KB
mochi
17 KB
Lighthouse score
ogygia
92
sveltekit
85
astro
95
mochi
92
LCP (avg)
ogygia
2.6 s
sveltekit
3.5 s
astro
2.2 s
mochi
2.6 s
CLS (avg)
ogygia
0.000
sveltekit
0.000
astro
0.000
mochi
0.000
JS eval (median)
ogygia
16 ms
sveltekit
13 ms
astro
21 ms
mochi
15 ms

Mobile Lighthouse, simulated throttling. Sizes are brotli/raw of all JS the page loads. Method ported from khromov/interactive-blogs-benchmark. Reproduce: pnpm run e2e is separate; see bench/.

Reading it

  • Islands stay flat. ogygia, Astro and Mochi ship the same JS whether the post is 9k or 71k words. SvelteKit’s whole-page hydration climbs with the prose.
  • ogygia vs the other islands. Astro’s Preact islands are the smallest; Mochi (Svelte 5 islands) is leaner than ogygia. ogygia trades a little more baseline runtime for a much larger feature surface: server islands, held/live/frozen regions, and full SvelteKit integration (see the matrix below).
  • The runtime is feature-selected. ogygia ships only the features your app uses; a load-only page never pays for the router, lakes, or persistence. We track the per-profile runtime size in e2e/bundle-size.snapshot.json so it can only shrink over time.

What you can build

Speed is easy to match with islands; the difference is surface area. Here are the four benchmarked frameworks, by what they let you build:

CapabilitySvelteKitAstroMochiogygia
SSR / prerender
Partial hydration (islands)❌ whole-page
Client JS flat as content grows
Islands are Svelte 5➖ (Preact et al.)
Per-component wake: load·idle·visible·interaction·media➖ (no interaction)?
Server islands (defer render to an endpoint)?
Held regions: region() values you place like data
Frozen regions (lakes): zero-JS interactive-context subtrees
Live / streaming regions?
Built on SvelteKit: its adapters, hooks, $app, remote functions❌ own framework❌ own framework
Opt-in SPA router➖ (MPA + view transitions)?
Markdown content collections built in➖ add mdsvex
Feature-selected runtime (ship only what the app uses)

? = not verified for the benchmarked version. The trade-off: ogygia carries a little more baseline runtime (the size table above) for the bottom third of this matrix, the region primitives and Kit-native integration nothing else here has.

Reproduce it

# hermetic (Docker): builds every framework + runs Lighthouse
docker compose -f bench/docker-compose.yml up --build

# isolated runtime size per feature profile (rolldown, brotli)
pnpm run bundle-size

Numbers on this page are regenerated from bench/results/latest.json, the exact JSON the benchmark emits.