"use client" island
Components in src/components/ or src/islands/ with a top-level "use client" directive are bundled for the browser. The component returns an HTML string; the mount runtime stuffs it into the host and runs any embedded <script>. This is bext's React-island analogue — coarser-grained than signals (re-render replaces the whole inner DOM), but ergonomic for self-contained widgets.
Tip
Re-rendering a "use client" island replaces its entire inner DOM — any selectors or listeners set up between mounts are lost. Prefer signals when only individual values change; reach for "use client" for stateful widgets or those that depend on third-party libraries.
Output
count: 7
"use client";
// Component returns a string. The mount runtime stuffs the HTML
// into the host element AND runs any inline <script> blocks the
// component embeds — that's where interactivity gets wired up.
export default function ClientCounter(props: { initial?: number }): string {
const initial = props.initial ?? 0;
const id = "cc-" + Math.random().toString(36).slice(2, 8);
return `<div id='${id}'>
<p>count: <strong data-c>${initial}</strong></p>
<button data-act='inc'>+1</button>
<button data-act='dec'>−1</button>
</div>
<script>(function(){
var r = document.getElementById('${id}');
var n = ${initial};
var c = r.querySelector('[data-c]');
r.addEventListener('click', function(e) {
var t = e.target.closest('[data-act]');
if (!t) return;
n += t.dataset.act === 'inc' ? 1 : -1;
c.textContent = String(n);
});
})();</script>`;
}import { island } from "@bext-stack/framework/island";
import { Raw } from "@bext-stack/framework/jsx-runtime";
import ClientCounter from "../../../components/ClientCounter";
export default function Page() {
return (
<Raw html={island("ClientCounter", { initial: 7, nonce: 42 }, ClientCounter({ initial: 7, nonce: 42 }))} />
);
}