Reactive Islands

Note: Reactive islands are a Dataflow-only feature. Observable Framework processes entire pages at build time and doesn't support isolated reactive scopes or runtime transformation. This makes Dataflow well-suited for progressive enhancement and HTML-over-the-wire architectures.

By default, all reactive scripts on a page share a single reactive scope - top-level variables can be referenced from anywhere. Reactive islands create isolated scopes where variables are contained within the island. This is perfect for:

Basic Usage

Add is="reactive-island" to any container element to create an isolated reactive scope:

<div is="reactive-island">
    <script type="module" is="reactive">
        const count = mutable(0);
        const increment = () => count.value++;
        const decrement = () => count.value--;
    </script>
    <script type="module" is="reactive">
        <div>
            <button onclick={decrement}>-</button>
            <span style="margin: 0 1em">{count}</span>
            <button onclick={increment}>+</button>
        </div>
    </script>
</div>

Live Example: Multiple Islands

Each island below contains identical code - the same count, increment, and decrement declarations. Because each island has its own reactive scope, these variables don't conflict:

Island A

Island B

Island C

Deployment tip: When using islands, prefer import maps over bundling. With a bundler, each island gets its own copy of the runtime. With an import map, all islands share the same pre-minified runtime (). See Setup for details.

Integration with HTMX

Reactive islands work perfectly with HTMX. When HTMX swaps in new HTML containing a reactive island, the island automatically initializes:

<!-- Server returns this HTML fragment -->
<div is="reactive-island" hx-swap-oob="true" id="widget">
    <script type="module" is="reactive">
        const value = view(<input type="range" min="0" max="100"/>);
    </script>
    <script type="module" is="reactive">
        `Value: ${value}`
    </script>
</div>

Integration with Unpoly

Unpoly can swap reactive islands just like any other fragment:

<a href="/widget" up-target="#widget">Load Widget</a>

<div id="widget">
    <!-- This will be replaced with a reactive island -->
</div>

Integration with Datastar

Datastar uses Server-Sent Events to stream HTML updates. Reactive islands can be streamed as complete components:

<!-- Streamed via SSE -->
<div is="reactive-island" data-star-swap="innerHTML">
    <script type="module" is="reactive">
        // Self-contained reactive component
    </script>
</div>

How It Works

When the HTML transformer encounters a is="reactive-island" element:

  1. All is="reactive" scripts within are collected
  2. A dependency graph is built for just those scripts
  3. The runtime code is injected at the end of the island (not the body)
  4. Variables are scoped to the island - no conflicts with other islands or page-level scripts

Benefits Over Full SPA Frameworks

Aspect Full SPA Reactive Islands
Initial page load Blank until JS loads Instant server-rendered content
SEO Requires SSR setup Works out of the box
Bundle size Large framework overhead for runtime
Hydration Full page hydration Only interactive parts
Complexity Build pipeline required Optional - works at runtime