hydrate

Hydration strategies

When the JavaScript arrives is the whole point of an island. These are real regions on this page. Each block ships static HTML first; the JS toggle shows exactly what the server sent before hydration took over.

hydrate: 'load'

Hydrates as soon as the region's custom element connects, after DOM ready. Use it for the controls a page cannot function without. The JS-off side is the shipped HTML — present, styled, but inert.

hydrate: 'load'
import Panel from '$lib/Panel.svelte' with {
  hydrate: 'load'
};

<Panel />
live

Live since —

hydrate: 'idle'

Waits for requestIdleCallback (with a timeout fallback). The HTML is already there; only the listeners and reactive runtime wait for a quiet moment.

hydrate: 'idle'
import Widget from '$lib/Widget.svelte' with {
  hydrate: 'idle'
};

<Widget />
live

Idle after …

--:--:--

hydrate: 'visible'

Gated on IntersectionObserver. Below-the-fold content stays SSR HTML until it approaches the viewport. A visible.margin pre-warms it slightly early.

Scroll until the visible block intersects the viewport.
hydrate: 'visible'
import Chart from '$lib/Chart.svelte' with {
  hydrate: 'visible'
};

<Chart />
live

In view · —

hydrate: '(max-width: 600px)'

Any media query is a strategy, resolved with matchMedia. On a wide window this stays static until you narrow it — that is the strategy working, not a broken preview. Mobile-only JS never loads on desktop.

hydrate: media
import Drawer from '$lib/Drawer.svelte' with {
  hydrate: '(max-width: 600px)'
};

<Drawer />
live

0px · no match

preset: 'demo'

Option tuning cannot go on the import. It lives in plugin config, optionally behind a named preset. This block uses the demo preset from this site's vite config (hydrate: 'visible' with a 200px margin).

preset: 'demo'
// vite.config.ts
ogygia({
  presets: {
    demo: { hydrate: 'visible', margin: '200px' }
  }
});

// component
import Panel from '$lib/Panel.svelte' with {
  preset: 'demo'
};
live

In view · —