Îlots serveur
Sur cette page
Parfois, une zone d’une page par ailleurs statique a besoin de données fraîches à chaque requête — les commentaires sous un article de blog mis en cache, un badge de stock sur une fiche produit. Les îlots serveur gardent la page statique et ne font rendre que cette zone sur un serveur, au moment de la consultation.
1. Écrivez l’îlot
Un îlot est un module de fragment placé dans src/islands/ — un simple fichier .js ou .ts (pas .ht.js, car les îlots sont des fragments, pas des pages). Même idée que partout ailleurs dans sitelo : une fonction qui renvoie du HTML.
export default async function comments({ props, request }) {
const comments = await fetchComments(props.postId)
return `<ul>${comments.map((c) => `<li>${c.text}</li>`).join('')}</ul>`
}Il reçoit { name, props, request } et doit renvoyer une chaîne HTML. Les modules d’îlot sont exclusivement côté serveur — le code non référencé sous src/ n’atteint jamais le navigateur.
2. Placez-le dans une page
Importez island() depuis sitelo/islands. Le build statique livre le HTML de repli ; les props sont intégrées dans l’emplacement réservé, alors gardez-les petites et non secrètes.
import { island } from 'sitelo/islands'
export default ({ params }) => `
<html>
<body>
<article>…contenu statique…</article>
${island('comments', { postId: params.slug }, '<p>Chargement des commentaires…</p>')}
<script type="module" src="/islands.js"></script>
</body>
</html>
`import { html, body, article, script } from 'javascript-to-html'
import { island } from 'sitelo/islands'
export default ({ params }) =>
html(
body(
article('…contenu statique…'),
island('comments', { postId: params.slug }, '<p>Chargement des commentaires…</p>'),
script({ type: 'module', src: '/islands.js' }),
),
)import { island } from 'sitelo/islands'
export default function Post({ params }) {
return (
<html>
<body>
<article>…contenu statique…</article>
{island('comments', { postId: params.slug }, '<p>Chargement des commentaires…</p>')}
<script type="module" src="/islands.js" />
</body>
</html>
)
}3. Ajoutez le chargeur client
Un petit script récupère chaque fragment rendu et l’insère. Il passe par le pipeline de ressources habituel : une simple entrée src/islands.js suffit.
import { mountIslands } from 'sitelo/islands/client'
mountIslands()Dans sitelo (dev) et sitelo preview, cela fonctionne déjà — les deux servent les îlots sur /_sitelo/islands/<name> depuis src/islands/. Preview charge des modules .js / .mjs natifs (comme un hôte Node) ; les îlots TypeScript sont pris en charge en développement grâce à Vite.
Production
Votre hébergeur statique continue de servir les pages. Montez un petit gestionnaire là où vous exécutez du code serveur — Node, serverless ou une fonction edge — et il rendra les mêmes modules d’îlot. Pour un parcours complet avec un hôte Node exécutable et des exemples Netlify et Vercel, voir l’exemple d’îlots serveur.
// p. ex. un serveur Node, ou une fonction serverless/edge
import { createIslandsHandler } from 'sitelo/islands/server'
const handleIslands = createIslandsHandler({
islands: {
comments: () => import('./src/islands/comments.js'),
},
})
// Web Request → Response | null (null = pas une requête d’îlot)
export default { fetch: (request) => handleIslands(request) }Avec le http de Node ou express, utilisez plutôt createIslandsNodeHandler(options) — mêmes options, signature (req, res, next). Vous pouvez câbler automatiquement chaque module .js / .mjs de src/islands/ avec createIslandsFromDirectory. Si le chargeur interroge une autre origine ou un autre chemin, passez mountIslands({ endpoint: 'https://api.example.com/islands' }) et faites-le correspondre à l’option endpoint du gestionnaire.
Points de stabilité
island()/mountIslands()/createIslandsHandler/createIslandsNodeHandler— API publique, considérée comme stablesiteloetsitelo previewservent tous deux/_sitelo/islandsdepuissrc/islands/createIslandsFromDirectory— la même table pour les hôtes Node et pour preview (natif.js/.mjs/.cjs)- Exemples d’hôtes dans exemple d’îlots serveur :
server.jspour Node, fonction Netlify + rewrite, serverless Vercel + rewrite - Les props restent petites et non secrètes (chaîne de requête GET) — c’est intentionnel ; récupérez les secrets à l’intérieur de l’îlot, sur le serveur
- Les props viennent du client — validez-les, ou signez-les avec
SITELO_ISLANDS_SECRET
Stratégies de chargement
Par défaut, chaque îlot se charge dès que la page charge : une page avec huit îlots déclenche donc huit requêtes simultanées pendant le premier rendu. Passez when pour différer ceux qui ne sont pas immédiatement visibles.
// Charge dès que la page charge — le comportement par défaut.
island('cart', { id }, '<p>…</p>')
// Attend un callback d’inactivité.
island('recommendations', { id }, '<p>…</p>', { when: 'idle' })
// Attend que l’élément entre dans le viewport.
island('comments', { postId }, '<p>Chargement des commentaires…</p>', {
when: 'visible',
rootMargin: '400px', // commence à charger 400px en avance
})'load'(par défaut) — immédiatement, en même temps que tous les autres îlots'idle'— surrequestIdleCallback(avec repli sur un délai)'visible'— quand l’élément entre dans le viewport, via IntersectionObserver
rootMargin ne s’applique qu’à 'visible' et vaut '200px' par défaut. Les îlots expirent aussi plutôt que de tourner indéfiniment :
mountIslands({
timeout: 5000, // par îlot ; 0 désactive. Par défaut 10000
rootMargin: '300px', // valeur par défaut pour les îlots `when: 'visible'`
})mountIslands() se résout une fois les îlots immédiats stabilisés — les îlots différés se chargent ensuite d’eux-mêmes et ne sont volontairement pas attendus. Un îlot en échec ou expiré conserve son HTML de repli.
Les props sont des entrées non fiables
Les props d’un îlot viennent du client. Elles sont intégrées dans la page, renvoyées dans la requête, et n’importe qui peut les modifier au passage :
GET /_sitelo/islands/profile?props={"userId":"someone-else"}Traitez les props que reçoit votre îlot exactement comme un paramètre de requête — validez-les, et ne vous en servez jamais pour aller chercher des données auxquelles le visiteur n’a pas déjà droit.
Quand les props sélectionnent des données privilégiées, signez-les. Définissez un secret et sitelo signe chaque emplacement réservé au build, rejetant tout le reste avec un 403 :
SITELO_ISLANDS_SECRET=$(openssl rand -hex 32) sitelo buildLa même variable est lue par sitelo, sitelo preview et createIslandsHandler, si bien que développement, preview et production s’accordent. Donnez le même secret à votre hôte de production. Vous préférez le définir dans le code ?
import { configureIslands } from 'sitelo/islands'
configureIslands({ secret: process.env.MY_SECRET })Les signatures sont des HMAC-SHA256 sur le nom de l’îlot et ses props : une signature émise pour un îlot ne peut donc pas être rejouée sur un autre. Signer prouve que les props viennent de votre build — cela ne les cache pas, elles doivent donc rester non secrètes. Sans secret, les props sont acceptées telles quelles et leur validation incombe entièrement à votre module d’îlot.
Bon à savoir
- Aucun point de terminaison d’îlots déployé ? Le HTML de repli reste simplement en place — les pages se dégradent élégamment.
- Les requêtes sont des
GETavec les props dans la chaîne de requête, donc les réponses sont cacheables — réglez l’optioncacheControldu gestionnaire si vous voulez qu’un CDN retienne brièvement les fragments. - Pendant le chargement, l’emplacement réservé porte
data-sitelo-island-state="loading"(puisloadedouerror) — pratique pour le CSS.