Islands
Islands architecture
The server renders your full page as HTML, then only the pieces you mark as islands hydrate in the browser. Everything else ships zero JavaScript.
Why islands
A page full of interactive widgets doesn't need to ship one giant bundle. Each island is a small, self-contained hydration entry: it imports only what it needs, hydrates in place, and nothing on the page outside an island is ever re-rendered on the client. That keeps first paint fast and the JS budget predictable. The build step logs every island bundle it generates:
$x build[x] building island bundles...[x] ✓ / LikeButton, SearchWidget[x] ✓ /blog/[slug] LikeButton[x] build complete in 1.1s -> .x
Creating an island
Wrap a component in <Island> and register it on the same page or layout with export const islands. Only registered islands get a hydration bundle, so an unregistered component never ships client JS even if you wrap it.
import { useState } from "react";export default function LikeButton() { const [count, setCount] = useState(0); return ( <button onClick={() => setCount((c) => c + 1)} className="rounded-full border px-4 py-2"> Like {count} </button> );}import { Island } from "@thexjs/core";import { LikeButton } from "../../components/like-button";export const islands = { LikeButton };export default function BlogPost({ post }) { return ( <article> <h1 className="text-3xl font-bold">{post.title}</h1> <div>{post.body}</div> <Island name="LikeButton" client="visible"> <LikeButton /> > </Island> </article> );}Hydration triggers
The client prop picks when the island hydrates:
- client="visible" hydrates when the island scrolls into view. Good for content below the fold.
- client="idle" hydrates when the browser goes idle. Good for widgets the user isn't waiting on.
- client="load" hydrates immediately on page load. Use for the first thing the user interacts with.
How hydration works
At build time X bundles each island separately, server-renders the page to static HTML with the island's markup inline, and drops in a small loader script. In the browser the loader fetches the island's chunk on demand and hydrates only that subtree. Islands can be nested and reused across routes; each registered component gets exactly one entry per route.