AbsoluteJS

Islands

AbsoluteJS islands let one SSR-first page host interactive components from React, Vue, Svelte, and Angular without turning the whole page into a client app. Page handlers stay pure. AbsoluteJS owns the SSR markers, manifest wiring, bootstrap, hydration, and cross-framework runtime.

#Overview

Use islands when most of a page should remain server-rendered HTML, but a few areas need interactivity. A host page can stay in its own framework while selectively embedding islands from other frameworks.

Host pages stay simplerender normal SSR pages and drop islands in where you need client behavior.
Cross-framework is normala React host can render Vue, Svelte, or Angular islands.
State is store-firstislands do not take state keys in their props. Shared state comes from importing the same island store across components.
Zustand powers the store layerAbsoluteJS uses zustand/vanilla underneath and exposes framework-specific selectors on top.

#Registry

Define a registry once with direct component imports. This is the source of truth for SSR lookup and for the optional typed wrappers created with createTypedIsland(registry).

TS
import { defineIslandRegistry } from '@absolutejs/absolute/islands';
import { createTypedIsland as createTypedAngularIsland } from '@absolutejs/absolute/angular';
import { createTypedIsland as createTypedReactIsland } from '@absolutejs/absolute/react';
import { createTypedIsland as createTypedSvelteIsland } from '@absolutejs/absolute/svelte';
import { createTypedIsland as createTypedVueIsland } from '@absolutejs/absolute/vue';
import { AngularCounter } from '../angular/components/AngularCounter';
import { ReactCounter } from '../react/components/ReactCounter';
import SvelteCounter from '../svelte/components/SvelteCounter.svelte';
import { VueCounter } from '../vue/components/VueCounter';

export const islandRegistry = defineIslandRegistry({
  angular: { AngularCounter },
  react: { ReactCounter },
  svelte: { SvelteCounter },
  vue: { VueCounter }
});

export const TypedAngularIsland = createTypedAngularIsland(islandRegistry);
export const TypedReactIsland = createTypedReactIsland(islandRegistry);
export const TypedSvelteIsland = createTypedSvelteIsland(islandRegistry);
export const TypedVueIsland = createTypedVueIsland(islandRegistry);

The registry accepts real component values directly. No source strings, prop builders, or generated binding files are required in app code.

#Loose vs Typed

Each framework exports a built-in Island for loose authoring and a createTypedIsland(registry) helper for registry-driven typing. The loose primitive is useful when you want dynamic or ad hoc rendering. The typed wrapper is the strongest option when you want compile-time enforcement of the full frameworkcomponent props chain.

TSX
import { Island } from '@absolutejs/absolute/react';

export const DashboardPage = () => (
  <main>
    <Island
      framework="vue"
      component="VueCounter"
      hydrate="visible"
      props={{
        initialCount: 0,
        label: 'Loose deferred Vue island'
      }}
    />
  </main>
);
TSX
import { TypedReactIsland } from '../../islands/registry';

export const DashboardPage = () => (
  <main>
    <TypedReactIsland
      framework="react"
      component="ReactCounter"
      hydrate="load"
      props={{
        initialCount: 0,
        label: 'React island'
      }}
    />
  </main>
);
TSX
// Loose Island accepts any framework/component string pair.
<Island
  framework="vue"
  component="VueCounter"
  props={{ initialCount: 0, label: 'Loose island' }}
/>

// Typed islands are driven by the registry.
<TypedReactIsland
  framework="react"
  component="ReactCounter"
  props={{ initialCount: 0, label: 'Typed island' }}
/>

// createTypedIsland(registry) narrows all three parts together:
// framework -> component -> props
//
// These fail at compile time:
// - wrong framework/component combinations
// - missing required props
// - extra props not accepted by the component
Loose Islandruntime-safe, flexible, and useful when the framework/component pairing is chosen dynamically.
Typed island wrappersexact component names and prop shapes inferred from the registry for that framework.

#Island Stores

Shared state is not passed through island props. Instead, define a real store with createIslandStore(...) and import that same store into any island component that should share it. This follows the same store-first mental model you would use with Zustand itself.

TS
import { createIslandStore } from '@absolutejs/absolute/islands';

export const counterIslandStore = createIslandStore(
  'counter',
  {
    sharedCount: 0
  },
  (set) => ({
    incrementShared: () =>
      set((state) => ({
        sharedCount: Number(state.sharedCount ?? 0) + 1
      })),
    resetShared: () => set({ sharedCount: 0 })
  })
);
TSX
// Import the same island store module from any island component.
// That is what makes the state shared across frameworks.

<Island
  framework="react"
  component="ReactCounter"
  props={{ initialCount: 0, label: 'React island' }}
/>

<Island
  framework="vue"
  component="VueCounter"
  props={{ initialCount: 0, label: 'Vue island' }}
/>
TXT
AbsoluteJS uses zustand/vanilla under the hood.

The model is store-first, not key-first:
- define state and actions once with createIslandStore(...)
- import the same store into any island component
- select state and actions with framework-specific primitives

Serializable state is snapshotted for SSR.
Actions are recreated from your store definition on the client.

#Hydration Modes

Hydration is per island, not page-wide.

TSX
<Island framework="react" component="Hero" hydrate="load" props={{ ... }} />
<Island framework="vue" component="AnalyticsPanel" hydrate="idle" props={{ ... }} />
<Island framework="svelte" component="PricingTable" hydrate="visible" props={{ ... }} />
<Island framework="react" component="StaticBadge" hydrate="none" props={{ ... }} />
loadhydrate immediately after bootstrap.
idlewait for browser idle time.
visiblewait until the island enters the viewport.
nonerender SSR HTML only with no client hydration.

#HTML & HTMX

HTML and HTMX hosts use the platform-native <absolute-island> custom element instead of a component import. AbsoluteJS lowers it into SSR island markup and wires the runtime automatically.

HTML
<absolute-island
  framework="react"
  component="ReactCounter"
  hydrate="load"
  props='{"initialCount":0,"label":"React island"}'
></absolute-island>

Because HTML attributes are strings, HTML and HTMX do not get the same prop-level inference as createTypedIsland(registry)on TS-based component hosts. They still use the same runtime, SSR, hydration modes, and island stores.