React Islands: React-виджеты на чужой HTML-странице
Иногда React нужен не на всю страницу, а точечно: баннер, форма, счётчик. Host при этом — CMS, легаси или другой фреймворк. Поднимать SPA ради пары виджетов избыточно.
Архитектура islands: страница остаётся обычным HTML, а интерактив монтируется отдельными «островами». Библиотека react-islands фиксирует простой контракт: разметка → регистрация → invoke().
Контракт
Host Библиотека data-react-island="<name>" invoke() находит узел и монтирует renderer data-react-island-<attr> уходит в props, если указан в attributes — при mount ставится data-react-island-inited
<div data-react-island="banner" data-react-island-title="Акция" data-react-island-href="/promo" > Fallback до JS </div>
Использование
import { useEffect } from 'react'; import { Runner, setComponent, getWrapper } from 'react-islands'; import type { ComponentInvokeProps } from 'react-islands'; function Banner({ title, href, innerHTML, onMount }: ComponentInvokeProps & { title?: string; href?: string; }) { useEffect(() => { onMount?.(); }, [onMount]); return ( <a href={href}> <strong>{title}</strong> {innerHTML && <span dangerouslySetInnerHTML={{ __html: innerHTML }} />} </a> ); } setComponent('banner', (element) => { getWrapper({ element, component: Banner, attributes: ['title', 'href'] }); }); Runner.getInstance().invoke();
Повторный invoke() безопасен: already inited узлы пропускаются.
Динамика
const el = document.createElement('div'); el.setAttribute('data-react-island', 'banner'); el.setAttribute('data-react-island-title', 'Ещё баннер'); container.append(el); runner.render(el); // смонтировать один runner.unmount(el); // снять root перед удалением из DOM el.remove();
Когда брать
Да: виджеты на CMS/легаси, props из разметки, отдельный bundle островов.
Нет: уже есть React SPA или нужна общая store-шина между виджетами.
Код и демо: gitflic.ru/project/yamakca/react-islands.








