Islands of any framework
PAD writes the page and marks the islands; one small runtime mounts a component of any framework on each; Vite builds the components and {vite} links the build.
1. The island, in the template
An element names its component and carries its props - the ^ sigil writes a field as JSON for the
attribute - and data-load says when its code is fetched. What stands inside shows
until the component takes over.
<div data-island="VueCounter" data-load="visible" data-props="{^counter}">
<p>{$counter.start}</p>
</div>
{vite 'src/main.js'}
2. The registry, in the build
_frontend/src/main.js names every island and pairs it with its framework. Both are
fetched by import() when an island of the name is due - a page without a Vue island
loads no Vue:
// The islands of every page, by the name a template gives them in data-island. Each entry
// pairs a component with its framework; nothing of either is fetched before an island of
// that name is on the page and due to load (pad-islands.js).
import './theme.js';
import { islands, react, vue, svelte, preact, solid } from './pad-islands.js';
islands({
ReactCounter: react(() => import('./islands/Counter.react.jsx')),
VueCounter: vue(() => import('./islands/Counter.vue')),
SvelteCounter: svelte(() => import('./islands/Counter.svelte')),
PreactCounter: preact(() => import('./islands/Counter.preact.js')),
SolidCounter: solid(() => import('./islands/Counter.solid.jsx')),
Shelf: react(() => import('./islands/Shelf.react.jsx')),
CartSummary: vue(() => import('./islands/CartSummary.vue')),
CartBadge: svelte(() => import('./islands/CartBadge.svelte')),
OrderCard: react(() => import('./islands/OrderCard.react.tsx')),
LoadLog: preact(() => import('./islands/LoadLog.preact.js')),
Clock: solid(() => import('./islands/Clock.solid.jsx'))
});
3. An adapter per framework
An adapter is a dozen lines: it mounts a component on an element with props and answers how to unmount it. Each is a module of its own, so it travels with its framework:
import { mount, unmount } from 'svelte';
export const name = 'Svelte';
export function island(Component) {
return (element, props) => {
element.innerHTML = '';
const instance = mount(Component, { target: element, props });
return () => unmount(instance);
};
}
4. Talking to PAD, and to each other
pad-islands.js also exports get(page, params) - the JSON of a page's
$padExpose - post(page, body) with the session's CSRF token, and
store(name, initial): state any framework subscribes to. A React island writes it, a
Vue and a Svelte island show it.
5. Build and dev server
cd apps/islands/_frontend
npm install
npm run build # www/islands/build/ with .vite/manifest.json
npm run dev # the dev server; build/hot holds its address
{vite 'src/main.js'} reads the manifest and writes the entry's CSS, its chunks as
modulepreload and its module script. While npm run dev runs, a hook of
vite.config.js writes the dev server's address into build/hot, and
{vite} asks the dev server instead - a save in the editor shows at once.
Each framework compiles its own files: *.react.jsx, *.solid.jsx, *.vue,
*.svelte. Preact needs no compiler - its components are written with htm.