Îlots serveur (Server Islands)

La coquille de la page est rendue une fois et mise en cache ISR ; le fragment personnalisé est rendu séparément par une route force-dynamic puis injecté côté client. Un seul bloc dynamique ne force donc plus toute la route en force-dynamic — la coquille reste sur le chemin rapide du cache et seul le trou est récupéré par requête.

Astuce
Sans les îlots serveur, un seul bloc personnalisé (menu connecté, panier, test A/B) force force-dynamic sur toute la page → aucun cache ISR. Ici la coquille mise en cache contient un placeholder ; le loader inline récupère le fragment /api/server-islands-frag par requête. Rechargez : l'horodatage et le jeton du fragment changent à chaque fois, alors que la coquille, elle, reste servie depuis le cache.

Résultat

loading…

Le fragment ci-dessous est chargé par un petit script inline après le rendu de la coquille. Rechargez : il change à chaque fois ; la page autour reste en cache.

src/app/examples/server-islands/page.tsxTSX
import { Raw, ServerIsland } from "@bext-stack/framework";

// The page shell is ISR-cached (the demo site renders mode = "isr").
// Only the island below is dynamic — fetched per request.
export default function Page() {
  return (
    <div>
      <h1>My (cached) page shell</h1>
      <Raw
        html={ServerIsland({
          src: "/api/server-islands-frag",
          fallback: "<em>loading…</em>",
          on: "load", // or "visible" | "idle"
        })}
      />
    </div>
  );
}
src/app/api/server-islands-frag/route.tsTypeScript
// src/app/api/server-islands-frag/route.ts
// The dynamic hole. MUST live under /api/ — a page.tsx shadows a
// nested-sibling route.ts, so the fragment gets its own segment.
export const dynamic = "force-dynamic";

export function GET() {
  const now = new Date().toISOString();
  const n = Math.floor(Math.random() * 100000);
  return new Response(
    "<strong>Personalized server island</strong> — rendered " + now +
      ", token " + n + ".",
    {
      headers: {
        "content-type": "text/html; charset=utf-8",
        "cache-control": "no-store",
      },
    },
  );
}