React

Strategie di idratazione delle isole in Astro

La direttiva client è una scelta di budget: load, idle, visible, media o only decidono quando scaricare il JavaScript dell’isola e quanto costa la pagina.

In sintesi

  • La direttiva più economica che mostra il contenuto in tempo è quasi sempre quella giusta.
  • client:visible si appoggia a IntersectionObserver: un’isola nascosta non idrata mai.
  • Il numero da misurare è il chunk dell’isola più il runtime del framework, non il peso della pagina.

In Astro la strategia di idratazione è una scelta di budget: si prende la direttiva più economica che porta il contenuto interattivo prima che l’utente lo tocchi, e si misura quanto JavaScript aggiunge.

Il costo di ogni direttiva

DirettivaJavaScriptEffetto percepito
client:loadScaricato subitoIl widget risponde al primo click
client:idleDopo il primo momento liberoRitardo breve, nessun blocco del render
client:visibleQuando l’isola entra nel viewportZero costo per chi non scorre
client:mediaQuando la query è veraIl desktop non paga il menu mobile
client:onlyNessun HTML dal serverNiente contenuto per i crawler

Le direttive accettano un valore: client:idle con timeout mette un tetto all’attesa e client:visible con rootMargin anticipa il caricamento di 200 pixel, così l’isola è pronta quando arriva sullo schermo.

---
import Search from "../components/Search.jsx";
import Gallery from "../components/Gallery.jsx";
---


Si misura sulla build, non a occhio

La build elenca un file per isola: il numero da guardare è il peso del chunk dell’isola sommato a quello del runtime del suo framework, non il totale della pagina.

npm run build
ls -lh dist/_astro/*.js

Quel totale non è un tetto per una pagina Astro: è il costo di un’applicazione interamente idratata. La differenza utile è quella fra due build della stessa pagina, una con l’isola e una senza, perché il runtime del framework si paga una volta e non per componente.

Quando l’isola non serve

Un blocco che si apre e si chiude non è un’isola: <details> fa lo stesso senza JavaScript. Un form che invia dati funziona con una form action. L’isola entra quando serve stato che il server non può calcolare.

Isola server, zero JavaScript

---
import UserGreeting from "../components/UserGreeting.astro";
---


  Hello, guest.

server:defer sposta l’HTML dell’isola in una richiesta separata: la pagina arriva subito con il fallback e il contenuto lo sostituisce quando è pronto. Non c’è idratazione, quindi nessun runtime, ma c’è una richiesta in più e un fallback visibile che va progettato.

Il tetto da tenere

Una pagina con tre isole dello stesso framework paga il runtime una volta sola ma tre componenti. Se la somma supera quella di un unico widget conviene fondere le isole, perché il confine costa più del codice.

La regola di decisione

Regola: parti da client:visible; scendi a client:idle quando il widget deve essere pronto prima che l’utente lo raggiunga, come una ricerca in testata; tieni client:load per il solo controllo che sta nella prima schermata e riceve un click immediato. client:only resta per il codice che non può girare sul server.

Il confine ha un prezzo fisso: due isole dello stesso framework scaricano il runtime una volta e due componenti, ma ognuna aggiunge un punto di ingresso e un ordine di esecuzione da verificare. Sopra le quattro isole in una pagina conviene chiedersi se il widget non sia uno.

Il limite da mettere in conto

client:visible si appoggia a IntersectionObserver: un’isola dentro un contenitore display: none o in una scheda chiusa non entra mai nel viewport e non idrata. Serve client:load oppure idratare al primo click sulla scheda.

Errori che si vedono

  • Mettere client:load sull’isola in fondo alla pagina: paghi subito un componente che serve dopo tre schermate.
  • Aspettarsi che client:visible risparmi su un’isola sopra la piega: entra nel viewport all’istante.
  • Contare sull’HTML di un’isola client:only: il server non la rende e il crawler trova il vuoto.
  • Misurare il peso della pagina invece del chunk dell’isola: la differenza sparisce nel totale.
  • Avvolgere in un’isola un componente che riceve solo props statiche: paghi il runtime per un DOM che il server sa già scrivere.

Le isole si riempiono con componenti pronti: saastro e Astro Association si installano come sorgente, e il blocco hero resta statico finché non riceve una direttiva. L’elenco delle direttive è in isole interattive in Astro, il caso in cui l’idratazione manca in client directive mancante, il confronto in Astro vs Next.js.

Domande frequenti

Quale direttiva scelgo per un widget sopra la piega?

client:load. Sopra la piega il widget entra nel viewport subito, quindi client:visible non risparmia nulla e aggiunge un giro di IntersectionObserver prima del caricamento.

client:idle ha un timeout massimo?

Sì, si passa come valore e senza di esso l’idratazione aspetta il primo momento libero, che su una pagina pesante può arrivare tardi. Un timeout di 500 millisecondi mette un tetto prevedibile.

Perché due isole non condividono lo stato?

Perché ognuna è un’applicazione separata con il proprio componente e il proprio stato. Per condividere dati serve uno store esterno, come nanostores, sottoscritto da entrambe.

Il framework runtime viene duplicato?

No, il codice condiviso del framework finisce in un chunk comune alle isole dello stesso tipo. Restano separati i componenti, che sono la parte che scrivi tu.

client:visible funziona nelle schede?

Solo quando la scheda è visibile: un pannello nascosto non interseca il viewport. Per quel caso conviene client:load sul pannello che si apre, oppure un’idratazione manuale al click.