Islas interactivas
Para lo que sí necesita un framework real, sin que el resto del sitio deje de ser HTML-first.
Marcar una isla
<div data-nexa-island="dashboard" data-nexa-strategy="load" data-nexa-props={{ tasks: data.tasks }}>
<p>Cargando…</p>
</div>
# nexa.toml
[imports]
dashboard = "/vendor/dashboard-island.js"data-nexa-island es un string literal, resuelto contra nexa.toml [imports] — Rust nunca abre ni interpreta el módulo al que apunta. El fallback (los hijos del div) se resuelve con el mismo mecanismo que cualquier otro contenido: data.*, t(), <For> incluido.
El contrato de mount()
export default function mount(
el: Element,
props: Record<string, unknown>,
): void | (() => void) {
el.textContent = String(props.tasks);
return () => { /* cleanup opcional */ };
}El módulo debe exportar por defecto una función mount(el, props) que opcionalmente devuelve una función de limpieza. Podés escribirla a mano con @nexa/reactivity, o montar un framework real.
Vue real dentro de una isla
// packages/vue-island
import { createApp } from "vue";
export function defineVueIsland(component) {
return function mount(el, props) {
const app = createApp(component, props);
app.mount(el);
return () => app.unmount();
};
}Un componente real de Vue (u otro framework), sin que Rust lo lea ni lo entienda jamás — corre 100% en el cliente. Un Vue Router montado adentro convive sin conflicto con la navegación SPA de Nexa.
Una librería de npm dentro de una isla
// src/islands/markdownPreview.island.ts
import { marked } from "marked"; // import normal, se bundlea con esbuild
export default function mount(el, props) {
const textarea = document.createElement("textarea");
const preview = document.createElement("div");
const render = () => { preview.innerHTML = marked.parse(textarea.value); };
textarea.addEventListener("input", render);
el.append(textarea, preview);
render();
}Distinto de [imports]: un archivo de isla se bundlea completo con esbuild (--bundle --format=esm), así que un import normal en la parte de arriba se resuelve como en cualquier proyecto TypeScript.
Modelo de componentes liviano (solo en islas)
import { mountComponent, type Component } from "@nexa/reactivity";
const Item: Component<{ label: string }> = (el, props) => {
el.textContent = props.label;
};
export default function mount(el, props) {
return mountComponent(Item, el, { label: props.label });
}No aplica a páginas Nexa Core. Component<P>, mountComponent(), y un ComponentContext que encadena la limpieza de hijos a la del padre — sin Virtual DOM, exclusivamente para código dentro de una isla nativa.