Server Islands

Server islands let you render expensive or dynamic components on the server without blocking the initial page shell. The page loads instantly with a placeholder, then the island streams in when the server-side data is ready.

Different from "use client" and "use signals" islands. Server islands are about deferred server rendering — the component still runs on the server, just streamed late so the shell ships first. For interactive islands that hydrate in the browser, see "use client" (React-based) or "use signals" (no React, ~3 KB).

How It Works

Mark a component as a server island with the island directive:

// components/ProductReviews.tsx
"use island";

export default async function ProductReviews({ productId }: { productId: string }) {
  // This fetch happens on the server, after the page shell is sent
  const reviews = await db.reviews.findMany({ where: { productId } });

  return (
    <section>
      <h3>{reviews.length} Reviews</h3>
      {reviews.map((r) => (
        <div key={r.id}>
          <strong>{r.author}</strong>
          <p>{r.body}</p>
        </div>
      ))}
    </section>
  );
}
// pages/product/[id].tsx


export default function ProductPage({ params }: { params: { id: string } }) {
  return (
    <main>
      <h1>Product Details</h1>
      <p>This part renders immediately.</p>

      {/* This streams in after the data fetch completes */}
      
    </main>
  );
}

Rendering Behavior

1. Page shell — bext renders the full page, replacing islands with a <bext-island> placeholder element 2. Streaming — each island renders in parallel on the server via the V8 pool 3. Injection — completed islands stream into the page as <script> tags that replace their placeholder 4. Hydration — if the island has client-side interactivity, React hydrates it after injection

This means:

- Time to first byte is fast because the shell doesn't wait for slow data

- Islands render in parallel, not sequentially — a slow database query in one island doesn't block others

- SEO is preserved — the final HTML contains the full rendered content for crawlers

Configuration

# bext.config.toml
[render]
islands = true            # default: auto-detected from "use island" directives
island_timeout_ms = 10000 # timeout per island (renders fallback on timeout)
island_concurrency = 8    # max parallel island renders per request

Fallback Content

Provide a fallback that shows while the island is loading or if it times out:

Loading reviews...</p>} />

If the island fails or times out, the fallback stays in place and an error is logged. The rest of the page is unaffected.

When to Use Server Islands

Use case Recommendation
Personalized content (cart, user profile) Server island — avoids caching issues
Slow database queries Server island — doesn't block the page shell
Static content Regular SSR — no need to defer
Real-time data Combine with SSE via the real-time hub
Below-the-fold content Server island — improves perceived performance

Comparison with Streaming SSR

Server islands differ from React's built-in `` streaming:

- Islands are component-level — you mark individual components, not Suspense boundaries

- Islands run in parallel — the V8 pool distributes renders across workers

- Islands support timeout + fallback — graceful degradation is built in

- Islands work without React — they're a bext platform feature, not tied to a framework