Î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
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.
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.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",
},
},
);
}