ZOYKUHot Board

ogygiajs/ogygiaДокументация

ogygiajs/ogygia documentation on GitHub Hot

Языковое уведомление:Перевод на данный язык отсутствует. Отображается официальный английский оригинал.
npm i ogygia

ogygia

ogygia

SSR islands for SvelteKit — no Kit patches.
Docs → ogygiajs.dev

SvelteKit native · No Kit patches · SSR islands · Feature-selected runtime · load / idle / visible · Media queries · Server islands · Single-flight nav · Live regions · Lakes · Remote functions · Async Svelte · Streaming $page.data · Cross-island context · Form actions · SPA router · View Transitions · Link prefetch · Persist layout · devalue props · Import attributes · Named presets · Nesting · Prerender · csr=false HMR

No Kit client bootstrap — the shared runtime is a custom element plus the features your app actually uses (a load-only app is ~8 KB min+brotli; the SPA router, lakes, live regions, and persistence are bundled only when used). Mark components with an import attribute; only those get their own JS. Each hydrate island’s module URL is written onto <ogygia-region entry> (Astro-style), so the sticky runtime does not grow with app-wide island count. Not a new framework — it sits on SvelteKit.

Install

pnpm add ogygia
// vite.config.ts — ogygia MUST come before sveltekit()
import { sveltekit } from '@sveltejs/kit/vite';
import { ogygia } from 'ogygia/vite';
import { defineConfig } from 'vite';

export default defineConfig({
	plugins: [ogygia(), sveltekit()]
});
// src/routes/+layout.ts
export const csr = false;

Dev HMR still works with csr = false — soft updates for CSS and shared modules, full reload for route shells and island entry components. No extra setup; see the docs.

Example

<script>
	import Counter from '$lib/Counter.svelte' with { wake: 'load' };
	import Chart from '$lib/Chart.svelte' with { wake: 'visible' };
	import Greeting from '$lib/Greeting.svelte' with { render: 'deferred', fetch: 'load' };

	// Counter is a portable island binding — lists / dynamic <Comp /> work too
	const Dyn = Counter;
</script>

<Counter start={10} />
<Dyn start={1} />
<Chart />
<Greeting name="world">
	{#snippet ogygiaFallback()}<p>loading…</p>{/snippet}
</Greeting>

That’s an island (JS on load), the same island used dynamically, a below-the-fold island, and a server island (HTML fetched later). Lakes, presets, the SPA router, remotes, and the rest live in the docs:

ogygiajs.dev

Dynamic import() + region attributes

Not supported — ogygia fails the build if it sees import('./X.svelte', { with: { hydrate: 'load' } }) (same for defer / preset). Vite strips those attributes; runtimes reject unknown keys; islands need a static import … with { … } so SSR can emit a shell.

For a chunk that downloads only after a click, use a host island and plain await import('./Widget.svelte') (no region attributes) — that mounts a regular component, not a second island. Docs: pesky patterns · playground demo.

Trust boundaries and design constraints: INVARIANTS.md in the monorepo root.

Page data

$page.data — your load data, plus url / params / route / status / form / error — reads inside an island exactly like a Kit page:

<script>
	import { page } from '$app/state';
</script>

<p>Bonjour {page.data.user.name} — {page.url.pathname}</p>

A load that returns a promise streams into the island: the shell and the island’s {#await} pending branch paint immediately (first paint is never blocked on the slowest promise), and each promise resolves live as it settles. Rejections show {:catch}; custom transport types round-trip. Docs: page data.

Shared state & context

Each island is its own hydration root, so a value handed between islands crosses a serialization boundary. A live class opts in with a wire codec, and every island that receives it shares one live instance — mutate it in one, the others repaint:

<script>
	import Count from './Count.svelte' with { wake: 'load' };
	import Add from './Add.svelte' with { wake: 'visible' };
	import { Cart } from './cart.svelte.js'; // a class with a static `wire` codec

	const cart = new Cart();
</script>

<Count {cart} />
<Add {cart} />

To reach islands scattered down a subtree without prop drilling, provide it once as context — or adopt cross-island context in an existing csr = false layout by swapping import { setContext } from 'svelte' to from 'ogygia'. Docs: state & context.

Live regions

A region made from a with { region: 'raw' } import is awaitable. Await it on the server (or just yield it from a query.live — the language awaits it) and it renders to HTML there and travels with its markup, so the client swaps it in with no fetch:

export const dashboard = query.live(v.string(), async function* (id) {
	for await (const stats of feed(id)) yield region(StatCard, { stats });
});
<Region of={dashboard(id).current} />

Static dual regions (region: 'raw', no wake) morph in place across ticks; interactive ones (region: 'raw' + wake) keep-alive (new props pushed into the mounted island, local state intact). LiveView, with Svelte components.

Single-flight navigation

Each load-scheduled server-island hole fetches its own signed HTML on load. On a SPA navigation the router prescans the incoming page and pulls all its holes down one batch request — no fetch-per-hole waterfall. Holes stream back out of order (fast-first) as each settles, and a command that returns a region repaints in a single flight — one round trip that both mutates and repaints.

Content collections

RF-native content collections ship inside ogygia — content() mints Kit prerender / query / query.live remotes, and get(id) hands back { id, data, headings, body } where body is a region you render with <Region>. Import from ogygia/content; configure markdown via ogygia({ content: { markdown } }). mdsvex / shiki are optional peers.

License

MIT

Зеркало документации архивировано из официального репозитория GitHub · Перейти на GitHub
Дата снимка: 2026-11-09