FRbext.dev
DocsGuides
On this page

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:

tsx
// 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>
  );
}
tsx
// pages/product/[id].tsx
import ProductReviews from "../../components/ProductReviews";

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 */}
      <ProductReviews productId={params.id} />
    </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#

toml
# 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:

tsx
<ProductReviews productId={id} fallback={<p>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 (via Suspense boundaries):

  • 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
Tip

If you need interactive client-side behaviour inside an island (event handlers, local state), combine a server island with a "use client" or "use signals" island. The server island streams the initial HTML; the client island hydrates it. See Islands and Signals.

Note

Each island render occupies a slot in the shared V8 pool. If a page has many islands and the pool is saturated, additional islands queue rather than run truly in parallel. Tune island_concurrency and the pool size together (see V8 render pool).

Edit this page ↗Need a hand? ↗
Search documentation

Start with a topic, a command, or a question.