Skip to content

Regions

Who owns the DOM

Every node inside a region has one owner. ogygia repairs, morphs and watches only what Svelte's hydration reads, and leaves a web component's own nodes alone. This page shows how to place a third-party widget so nothing fights over it.

On this page

An island’s DOM does not belong to ogygia alone. Svelte binds parts of it when the island wakes, a web component rewrites its own light DOM when it upgrades, and a design-system script may strip whitespace while the island sleeps. To keep these from fighting, ogygia gives every node one owner, and everything ogygia does to the DOM starts by asking who owns the node in question. This matters most when you put a third-party widget, such as a web component or a vendor embed, inside an island, or when the devtools Hydration tab reports a conflict.

The owners

OwnerWhat it isWhat ogygia does with it
Svelte’s walkthe parts of the island’s markup Svelte’s hydration binds by positionbefore the wake, puts it back to the server’s shape if another script changed it
Staticmarkup Svelte never reads again: {@html} content, bind:innerHTML, a custom element whose children are staticleaves it alone before the wake; a hole answer or a live tick still updates it
The page (yours)an element you marked data-ogygia-keepnever touches it: not before the wake, not on a hole answer, not on a navigation
A web componentchildren and attributes a custom element added itselfkept on a hole answer, a live tick and a navigation
Lakewake: 'none' contentthe server’s HTML, lifted out of the way while the island hydrates
Slothost-page content an island adoptsnever repaired: Svelte adopts it whole

You don’t mark static markup yourself. The compiler finds {@html}, bind:innerHTML and custom elements with static children, and stamps them in the server HTML with data-og-opaque. How ogygia restores markup that Svelte’s walk owns is described in Client islands.

Placing a third-party widget

A widget that rewrites its own light DOM has to sit somewhere Svelte never reads. Any one of these placements works:

<script>
  // 1. inside {@html}: Svelte takes the content whole and never walks into it
  const markup = `<vendor-search id="s"><form action="/search">…</form></vendor-search>`;
</script>

<div class="search">{@html markup}</div>

<!-- 2. a custom element whose children are static: Svelte never descends -->
<vendor-search><form action="/search"><input name="q" /></form></vendor-search>

<!-- 3. marked as yours: ogygia never touches it, anywhere -->
<vendor-search data-ogygia-keep="search">…</vendor-search>

Put the widget inside the {@html} content rather than making it the element that holds the content, because Svelte keeps its own markers as the direct children of that element. The {@html} may sit beside other content (<div><h3>{title}</h3>{@html markup}</div>), in which case the elements in that <div> are compared by position only and never entered. An {@html} at the very top of a component has no element around it to mark, so wrap it in one.

Conflicts

If a web component changes its children in a place that Svelte’s hydration does read, such as a custom element whose children use {expressions} or blocks, both cannot win. Svelte binds nodes by position, so ogygia removes the nodes the component added and lets the island hydrate. It reports the conflict instead of hiding it: you get a console warning in development and a conflict finding in devtools that names the element and the fix, which is one of the three placements above.

What data-ogygia-keep means

On a plain element, data-ogygia-keep means that the live DOM owns this subtree. Before the wake, on a hole answer, on a live tick and on a navigation, ogygia matches the element and keeps it exactly as it is.

On an island, the keep import attribute means something related but different: the island survives a navigation. Svelte still owns that island, so it is repaired and hydrated like any other.