Skip to main content

Hydration Directives

Hydration directives let you control when an island becomes interactive using standard data-* attributes. This avoids loading JavaScript until it's actually needed.

Usage

Add data-island and data-hydrate attributes to any standard HTML element:

html
<!-- Hydrate immediately (default) -->
<nav data-island="/js/islands/nav.js" data-hydrate="load">
  ...
</nav>

<!-- Hydrate when browser is idle -->
<aside data-island="/js/islands/sidebar.js" data-hydrate="idle">
  ...
</aside>

<!-- Hydrate when scrolled into view -->
<div data-island="/js/islands/chart.js" data-hydrate="visible">
  <canvas>...</canvas>
</div>

Activating

Call hydrateIslands() once in your shell script:

js
import { hydrateIslands } from 'weblisk/core/hydrate.js';

hydrateIslands();

Strategies

| Strategy | Attribute | When |

|----------|-----------|------|

| load | data-hydrate="load" | Immediately on page load (default) |

| idle | data-hydrate="idle" | When the browser is idle (requestIdleCallback) |

| visible | data-hydrate="visible" | When the element enters the viewport (IntersectionObserver) |

| media | data-hydrate="media" + data-media="(max-width:768px)" | When a media query matches |

| event | data-hydrate="event" + data-event="click" | On the first occurrence of an event |

| never | data-hydrate="never" | Never hydrate (static only) |

Examples

Mobile-Only Island

html
<div data-island="/js/islands/mobile-menu.js" data-hydrate="media" data-media="(max-width:768px)">
  <button class="hamburger"></button>
</div>

The JavaScript only loads on mobile viewports.

Click-to-Activate

html
<div data-island="/js/islands/video-player.js" data-hydrate="event" data-event="click">
  <div class="video-placeholder">
    <img src="/thumb.jpg" alt="Video thumbnail" />
    <span>Click to play</span>
  </div>
</div>

The video player script loads only after the user clicks.

Below-the-Fold Content

html
<div data-island="/js/islands/comments.js" data-hydrate="visible">
  <section class="comments">
    <!-- Server-rendered comment HTML -->
  </section>
</div>

Comments hydrate only when the user scrolls down to them.

Declarative Event Binding

Bind events directly in HTML using data-on: attributes. The island module exports an actions object with named methods:

html
<div data-island="/js/islands/toolbar.js" data-hydrate="load">
  <button data-on:click="save">Save</button>
  <button data-on:click="reset">Reset</button>
  <input data-on:input="search" />
</div>
js
// js/islands/toolbar.js
export const actions = {
  save(e)  { /* save logic */ },
  reset(e) { /* reset logic */ },
  search(e) { /* filter on input */ },
};

Works with any DOM event: data-on:submit, data-on:change, data-on:keydown, etc.

How It Works

Each strategy uses a different browser API:

The island script must export a default function that receives the element:

js
// js/islands/my-island.js
export default function myIsland(el) {
  // el is the element with [data-island]
  el.querySelector('button').addEventListener('click', () => {
    // ...
  });
}