react-islands
Mount React components as islands into a host page DOM. Mark nodes with data-react-island, register renderers, call invoke() — each island hydrates independently.
Peer dependencies: react and react-dom ≥ 18.
Install
npm install react-islands
Example
Runnable demo in examples/: host HTML with banner and counter islands, plus runtime insert / unmount.
npm install
npm run example
Opens http://localhost:5173. Source aliases react-islands to src/, so library edits apply on refresh.
npm test
Contract
| Host | Library |
|---|---|
data-react-island="<name>" |
scanned by Runner.invoke(); looks up renderer via setComponent |
data-react-island-<attr>="…" |
passed as prop <attr> when listed in getWrapper({ attributes }) |
| — | on mount sets data-react-island-inited (synchronously when the root is created) |
Usage
1. Host markup — place islands anywhere in the page:
<div
data-react-island="banner"
data-react-island-title="Hello"
data-react-island-href="/promo"
>
Optional fallback / slot HTML
</div>
2. React component — receives mapped attributes plus innerHTML and onMount:
import { useEffect } from 'react';
import type { ComponentInvokeProps } from 'react-islands';
type BannerProps = ComponentInvokeProps & {
title?: string;
href?: string;
};
export function Banner({ title, href, innerHTML, onMount }: BannerProps) {
useEffect(() => {
onMount?.();
}, [onMount]);
return (
<a href={href}>
<strong>{title}</strong>
{innerHTML ? <span dangerouslySetInnerHTML={{ __html: innerHTML }} /> : null}
</a>
);
}
3. Register and invoke — after the DOM is ready:
import { Runner, setComponent, getWrapper } from 'react-islands';
import { Banner } from './Banner';
setComponent('banner', (element) => {
getWrapper({
element,
component: Banner,
attributes: ['title', 'href'],
});
});
Runner.getInstance().invoke();
Call invoke() once on initial load, or again after the host inserts new [data-react-island] nodes. Already mounted nodes (data-react-island-inited) are skipped. Call unmount(element) before removing a host node from the DOM.
API
setComponent(name, render)— register a renderer(element: HTMLElement) => void.getComponent(name)— look up a registered renderer.getWrapper({ element, component, attributes? })— unmount previous root if any, readdata-react-island-*attrs, clear the node, setdata-react-island-inited,createRoot+ render. PassesinnerHTML(pre-clear snapshot) andonMount(idempotent inited flag).unmount(element)/Runner.unmount(element)—root.unmount(), drop the stored root, removedata-react-island-inited.unmountAll()/Runner.unmountAll()— unmount every tracked island.Runner.getInstance().invoke()— find all pending[data-react-island]nodes and run their renderers.Runner.render(element)— mount a single host node (no-op if already inited).ISLAND_ATTR—"data-react-island".ISLAND_INITED_ATTR—"data-react-island-inited".