Hydratation paresseuse des îlots

Un îlot à signaux peut différer son hydratation hors du chemin critique. Le HTML rendu côté serveur est interactif visuellement immédiatement ; le runtime n'attache les liaisons réactives que lorsque c'est utile — "visible" (à l'entrée dans le viewport), "idle" (requestIdleCallback) ou "interaction" (premier pointeur / clavier / focus). Sans opts.lazy, l'îlot s'hydrate immédiatement — le comportement par défaut reste inchangé.

Astuce
C'est la part concrète de la « résumabilité » : on déplace le temps d'hydratation hors du time-to-interactive. Le balisage est déjà là (rendu serveur), donc l'îlot s'affiche tout de suite ; seul le branchement réactif attend. Eager par défaut → aucun îlot existant n'est affecté ; opt-in îlot par îlot via opts.lazy.

Résultat

Eager (par défaut) — hydraté au chargement.



count: 1 (×2 = 2)

Idle — hydraté dans un requestIdleCallback, après le rendu initial.



count: 4 (×2 = 8)

Interaction — hydraté au premier pointeur / clavier / focus.



count: 3 (×2 = 6)

Faites défiler vers le bas… ↓



count: 2 (×2 = 4)

Visible — hydraté à l'entrée dans le viewport (IntersectionObserver). Le compteur ci-dessus n'a été branché qu'au moment où vous l'avez fait défiler à l'écran.

API : signalsIsland(name, Component, props, opts?)opts.lazy"visible" | "idle" | "interaction". Absent = eager.

src/app/examples/lazy-hydration/page.tsxTSX
import { signalsIsland } from "@bext-stack/framework/signals";
import Counter from "../../../components/Counter";

export default function Page() {
  return (
    <div>
      {/* eager — hydrates on load (default, no opts) */}
      {signalsIsland("Counter", Counter, { initial: 1 })}

      {/* idle — hydrates in a requestIdleCallback, off the critical path */}
      {signalsIsland("Counter", Counter, { initial: 4 }, { lazy: "idle" })}

      {/* interaction — hydrates on first pointer / key / focus on the island */}
      {signalsIsland("Counter", Counter, { initial: 3 }, { lazy: "interaction" })}

      <div style="margin-top:120vh">
        {/* visible — hydrates when scrolled into view (IntersectionObserver) */}
        {signalsIsland("Counter", Counter, { initial: 2 }, { lazy: "visible" })}
      </div>
    </div>
  );
}