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. How to place a third-party widget so nothing fights over it.

On this page

An island’s DOM is not only ogygia’s. Svelte binds parts of it when the island wakes. A web component rewrites its own light DOM when it upgrades. A design-system script may strip whitespace while the island sleeps. ogygia gives every node one owner, and everything ogygia does to the DOM asks the same question first: who owns this node?

When to use

Read this page when you put a third-party widget (a web component, 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 do not mark static markup yourself. The compiler finds {@html}, bind:innerHTML and custom elements with static children, and stamps them in the server HTML (data-og-opaque).

Placing a third-party widget

A widget that rewrites its own light DOM must sit where Svelte never reads. Any one of these 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, not as the element that holds it: 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>); then the elements in that <div> are compared by position only, never entered. An {@html} at the very top of a component has no element around it to mark: wrap it in one.

Conflicts

If a web component changes its children in a place Svelte’s hydration does read (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 the island hydrates. It reports the conflict instead of hiding it: a console warning in development, and a conflict finding in devtools naming the element and the fix — one of the three placements above.

What data-ogygia-keep means

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

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