Astro vs Next.js : 133 Ko de JavaScript pour zéro composant client

Astro vs Next.js : 133 Ko de JavaScript pour zéro composant client

Le brief d'un challenge de développement ne nommait aucun framework. Il notait le temps de chargement deux fois dans sa grille d'évaluation. J'ai choisi Astro en trois secondes, sans rien mesurer. Le site est parti en production, un jury a inspecté le dépôt, et je n'avais toujours aucun chiffre pour justifier ce choix. Alors je les ai pris.

Il y a une phrase qu'on lit partout depuis trois ans : les Server Components de Next.js n'envoient quasiment plus de JavaScript au navigateur. Je l'ai mesurée sur une page sans un seul'use client', sans état, sans formulaire, sans un bouton — en Next.js 16 : 132 955 octets gzip, sur cinq requêtes. Avant que quoi que ce soit ne bouge à l'écran.

C'est le premier de trois résultats. Le deuxième dit qu'Astro avait raison. Le troisième dit que ça n'avait aucune importance.

Voilà le contexte. BenDjo est une marque béninoise d'infusions, de petit-déjeuner en entreprise et de traiteur. Le site est un catalogue avec panier, dont la commande part sur WhatsApp au lieu d'un paiement en ligne — le détail du projet est sur mon portfolio. Le brief énumérait ses exigences techniques — responsive, HTML sémantique, temps de chargement optimisé, panier vers WhatsApp, dépôt GitHub — et ne nommait aucun framework. La grille du jury, elle, notait le chargement deux fois : une fois en « Développement », une fois en « SEO ». Dans ce cas de figure, le réflexe par défaut c'est React. J'ai pris Astro.

Ce n'était pas un arbitrage réfléchi. Je voulais de la performance et de la vitesse de développement, j'ai suivi une recommandation, ça a marché, je suis passé à autre chose. Un choix d'architecture pris en trois secondes et jamais vérifié. C'est devenu la norme, et c'est exactement ce que cette collection existe pour attraper.

Le protocole

Trois builds, la même page des deux côtés.

Le contenu est identique : la home de BenDjo — hero, bandeau de réassurance, les trois fiches produits complètes, bloc de clôture — alimentée par le même fichierproducts.ts, copié tel quel dans le projet Next. Sortie 100 % statique des deux côtés (astro buildd'un côté,output: 'export'de l'autre).

Versions : Astro 7.1.3, Next 16.3.2, React 19.2.8, Node 22. Mesures du 25 août 2026. Toutes les tailles sont données brutes et engzip -9.

Deux précisions qui décident de la lecture des chiffres.

J'exclus le bundlenoModule. Next livre 112 594 octets de polyfills destinés aux navigateurs sans modules ES. Aucun navigateur moderne ne le télécharge. L'inclure gonflerait mon résultat de 39 Ko gzip sans que personne ne les paie. Je le sors.

J'ai refait le build Next avec les deux bundlers. Next 16 compile avec Turbopack par défaut. Un chiffre issu d'un seul bundler ne prouve rien sur le framework. J'ai donc rejoué avec Webpack, et je donne les deux.

« Next App Router n'envoie presque plus de JavaScript »

Faux. Et pas de peu.

Page sans aucune interactivité

requêtes JS

brut

gzip

Astro 7

0 (inline)

4 232 o

1 669 o

Next 16 · RSC pur · Turbopack

5

452 538 o

132 955 o

Next 16 · RSC pur · Webpack

4

446 029 o

130 616 o

Zéro'use client'dans le projet Next. Pas un seul composant client. Et 133 Ko partent quand même.

Les deux bundlers arrivent à 2 % l'un de l'autre. Dans cette configuration, le coût observé n'est donc pas un artefact de Turbopack.

Puis j'ai vérifié que ce chiffre ne venait pas de mon contenu. J'ai reconstruit un projet Next 16 vide: unlayout.tsx, unpage.tsxqui retourne<h1>Rien</h1>, aucune donnée, aucun import, aucune dépendance ajoutée. Le build sort 452 538 octets bruts, 132 955 octets gzip.

Le même nombre. Au bit près, sur les deux mesures.

Ce n'est donc pas un coût qui varie avec la page : c'est un plancher. Et on peut lire de quoi il est fait, en cherchant dans les deux plus gros fichiers :

chunk

gzip

ce qu'on y trouve

02fh7…js

71 459 o

createRoot,useSyncExternalStore,SuspenseList,onRecoverableError,react-dom

41qfc…js

48 157 o

84 occurrences deprefetch

React DOM et sa machinerie d'hydratation d'un côté, le routeur client de l'App Router de l'autre — c'est mon interprétation de ce que je lis dans les fichiers, pas une lecture du code source de Next. Ce que je peux affirmer sans interprétation : ces fichiers sont produits par le build et référencés par la page, alors que ma page de test ne contient aucun composant client et aucune logique interactive.

ChatGPT Image 27 août 2026, 21_37_11.png

L'affirmation n'est pas un mensonge, elle est mal découpée. Les Server Components suppriment le JavaScript que tu écris. Ils ne suppriment pas celui qui les exécute. On répète la première moitié de la phrase et on oublie la seconde.

Astro, en face, n'envoie aucun fichier JavaScript. Les 4 232 octets sont en ligne dans le HTML, donc zéro requête réseau supplémentaire.

« Astro envoie moins de JavaScript »

Vrai. Et sans le moindre effet observable sur ce site.

Voilà ce que transfère réellement la home de BenDjo :

brut

gzip

HTML (JavaScript en ligne compris)

32 677 o

8 544 o

CSS, un seul fichier

58 563 o

11 289 o

Images, 12 fichiers

814 006 o

Code transféré : 19 833 octets. Images : 814 006 octets. Sur les ressources que j'ai mesurées dans ce build, les images font 97,6 % du poids de la page. Un CDN, un cache navigateur ou des images servies en plusieurs tailles déplaceraient ce rapport — ils ne l'inverseraient pas.

Autrement dit : même en payant les 133 Ko de Next, le JavaScript serait resté minoritaire face aux photos de sachets d'hibiscus. La victoire d'Astro sur le poids est réelle, mesurable, et noyée.

ChatGPT Image 27 août 2026, 21_38_47.png

C'est le même raisonnement arithmétique que dans l'épisode précédent sur les pools de connexions : quand une chose pèse 2 % du total, l'optimiser parfaitement rapporte 2 % au maximum. Ici, c'est encore plus net — le JavaScript ne pèse pas assez pour que le choix de framework se voie dans le poids de la page.

Sauf qu'il y a un endroit où le poids transféré cesse d'être la bonne unité.

Une image lourde coûte du transfert et du décodage. Du JavaScript coûte en plus du parsing, de la compilation et de l'exécution — et cette exécution occupe le fil principal, celui qui doit répondre au premier tap. Ce n'est pas la même dépense, et elle ne se lit pas en kilo-octets.

Sur le site en production, le Total Blocking Time est à 0 ms, sur un Moto G Power simulé en 4G lente. Zéro, pas « faible ».

Et je dois m'arrêter là, parce que c'est la limite de mon protocole. Ce 0 ms mesure mon site Astro. Il ne mesure pas ce qu'aurait donné la version Next : je n'ai jamais fait tourner Lighthouse dessus. Je n'ai donc aucun droit d'écrire « Astro gagne sur le TBT ». J'ai un chiffre d'un côté et rien de l'autre.

Ce que je peux dire, c'est où la différence serait à chercher. Pas dans le poids transféré, qui est noyé par les images — mais dans le travail que le navigateur n'a pas à faire. Isoler ce gain demanderait un profilage comparable des deux versions. Je ne l'ai pas fait, et je ne vais pas conclure à sa place.

Le reste du rapport : Performance 99, Accessibilité 100, Bonnes pratiques 100, SEO 100. LCP 2,1 s — piloté par une image, évidemment. Données de terrain : aucune, le site est trop récent pour avoir un historique CrUX. Je n'ai donc que du Lighthouse simulé, et il faut le savoir.

« Astro t'oblige à réécrire à la main ce que React te donne »

Vrai. Et c'est le seul chiffre qui compte vraiment.

Le site a exactement un composant interactif : le tiroir panier. Persisté enlocalStorage, synchronisé entre les onglets ouverts, avec piège à focus, et une commande WhatsApp pré-remplie. Je l'ai écrit deux fois — une fois en JavaScript nu pour Astro, une fois en composant client React pour la mesure.

Le même panier

lignes de code

brut

gzip

Astro — écrit à la main

171

3 631 o

1 473 o

Next — client component

63

3 784 o

1 712 o

figure-4-panier.svg

Regarde les deux colonnes de droite : 153 octets d'écart. La fonctionnalité coûte le même poids des deux côtés. Ce qui diffère, ce sont les 108 lignes.

Et ces 108 lignes ne sont pas gratuites. En auditant mon propre code, j'ai trouvé dedans trois défauts. Je ne vais pas prétendre que React les aurait empêchés — React ne gère niinert, ni le piège à focus, ni la validation delocalStorage, et un développeur React peut écrire exactement les mêmes bugs. Ce sont trois défauts que je n'aurais probablement pas eu à écrire du tout, parce que les primitives de dialogue de l'écosystème React les ont déjà traités et fait auditer :

Aucun n'est dû à de la négligence. Ils viennent tous du même endroit : quand tu écris ton interactivité à la main, tu réimplémentes du comportement que d'autres ont déjà écrit, documenté et corrigé sur des milliers d'applications. Le gain n'est pas le framework en lui-même — c'est ce qui existe autour.

Il y a un deuxième coût, plus discret. En Next,'use client'est une déclaration explicite, vérifiée. En Astro, la frontière serveur/client est silencieuse : il a suffi qu'un<script>importe mon module de données pour que le catalogue produit entier et un tracé SVG de logo LinkedIn partent dans le bundle du navigateur. Rien ne m'a prévenu. Je l'ai vu parce que j'ai ouvert le bundle.

Le calcul qui relie les trois

On cherche toujours le point de bascule : à partir de combien de composants interactifs Astro devient-il plus lourd que Next ?

Avec mes chiffres, il n'existe pas.

Astro(N) = 1 473 × N
Next(N)  = 132 955 + 1 712 × N

Le coût par composant est plus élevé en Next aussi. Dans cette extrapolation, les deux droites ne se croisent jamais et l'écart se creuse : Astro reste devant quel que soit le nombre de composants.

Le vrai croisement est ailleurs :

Astro(N) = 171 lignes × N
Next(N)  =  63 lignes × N

Le coût d'écriture d'Astro grimpe 2,7 fois plus vite. Sur un composant, c'est 108 lignes — une soirée. Sur dix, c'est plus de mille lignes de gestion de focus, de persistance et de synchronisation que quelqu'un devra relire, tester et maintenir.

C'est ça, l'arbitrage. Pas des kilo-octets : des lignes de code et le nombre de bugs d'accessibilité qu'elles contiennent.

Une réserve, parce qu'elle est importante : ces deux formules sont extrapolées à partir d'un seul composant. Un site à dix composants partagerait de l'état entre eux, et cette mise en commun ne coûte pas la même chose dans les deux modèles. Je n'ai pas mesuré ce cas — je ne vais pas remplacer un chiffre mesuré par une projection présentée comme un chiffre.

Où mes conclusions s'inversent

Tout ce qui précède vaut pour un site vitrine à un composant interactif, servi en statique. Change un paramètre et ça bascule.

Si le site a besoin d'état partagé — authentification, panier serveur, personnalisation, tableau de bord — les 133 Ko de socle cessent d'être un ticket d'entrée pour devenir un investissement. Tu les paierais de toute façon, et tu récupères le routeur, l'hydratation et l'écosystème.

Si les images étaient optimisées à fond, le rapport 97,6 / 2,4 se resserrerait. PageSpeed me signale encore 297 Kio d'économies possibles. À poids d'images divisé par trois, le code passerait à ~7 % de la page. Toujours minoritaire, mais plus du bruit.

Si l'équipe ne connaît pas React, mes 108 lignes supplémentaires sont un faux problème : elles sont en JavaScript standard, lisibles par n'importe qui. Le coût d'apprentissage d'Astro est proche de zéro, celui de l'App Router ne l'est pas.

Et si tu mesures dans un an, les chiffres auront bougé. Un socle de framework, ça grossit et ça maigrit à chaque version majeure. C'est bien pour ça que je date mes mesures.

Douze jurys ont noté ce site

Le sprint s'est terminé par une correction. Douze jurys, une dizaine de dépôts, des grilles séparées : technique, design, SEO, communication. Ils ne se sont pas concertés. Leurs retours sont le seul contrôle externe que j'aie sur tout ce qui précède — et ils corrigent mes conclusions sur trois points.

Un. Le jury qui a lu le dépôt le plus attentivement a noté que c'était le seul projet du sprint à ne pas utiliser React. Il ne l'a pas mis dans les points forts. Il l'a mis dans les axes d'amélioration :

« Choix d'Astro plutôt que React : pertinent techniquement, mais à vérifier si le brief imposait une stack précise. »

Un correcteur qui a le brief sous les yeux a quand même eu besoin de vérifier. C'est le prix réel de sortir du défaut : même quand le choix est bon, il faut le justifier une fois de plus que les autres. Aucune de mes mesures ne dit ça.

Deux. Le jury SEO, qui a inspecté le site page par page, a écrit ceci :

« Le site est généré en pages statiques complètes et ne contient aucun JavaScript: tout fonctionne en HTML et CSS seuls, y compris le menu mobile. »

Sur le menu, il a raison — il s'ouvre avec une case à cocher et une règle CSS, sans une ligne de script. Sur le reste, il se trompe : la page d'accueil embarque 4 232 octets de JavaScript, le panier compris. Il ne les a pas vus, et c'est logique. Il n'y a aucun fichier.jsdans le dossier de build, aucune balise<script src>, aucune requête réseau dans l'onglet réseau. Tout est en ligne dans le HTML.

C'est le résultat le plus intéressant de cet article, et il ne vient pas de moi. L'inlining d'Astro ne supprime pas seulement les requêtes : il rend le JavaScript invisible à l'audit. Un auditeur professionnel a conclu à zéro. Il y en avait quatre kilo-octets. Retiens-le si tu mesures un site Astro : compter les fichiers JS te donnera toujours zéro.

Trois, et c'est le plus net. Sur douze grilles, deux jurys ont pointé le poids des images — « vérifier et maintenir l'optimisation des images », « servir plusieurs tailles selon l'écran allégerait encore l'accueil ». Aucun n'a pointé le JavaScript.

figure-5-poids-page.svg

C'est exactement ce que disaient les 97,6 %. Je l'avais calculé sur une seule page ; douze personnes qui ne se connaissaient pas sont arrivées à la même hiérarchie en regardant le site avec leurs propres yeux. Le même jury a d'ailleurs mesuré la feuille de style à « douze kilo-octets » — j'avais trouvé 11 289 octets gzip. On parlait bien de la même chose.

Reproduire l'expérience

Un chiffre qu'on ne peut pas rejouer n'est pas une mesure, c'est une opinion. Voilà de quoi refaire la mienne.

Machine : Linux x86-64, Node 22.23.1, npm 10.9.8. Builds du 25 août 2026.

Le socle Next, en cinq commandes — c'est le test qui donne les 132 955 octets sur une page qui ne contient rien :

mkdir next-min && cd next-min && mkdir app
echo "export default { output: 'export' };" > next.config.mjs
npm install next@16 react@19 react-dom@19 typescript @types/react @types/node
# app/layout.tsx : <html><body>{children}</body></html>
# app/page.tsx   : export default () => <h1>Rien</h1>
npx next build          # ajouter --webpack pour le second bundler

Côté Astro,npx astro buildsur le dépôt du site.

Le comptage se fait sur le HTML produit, pas sur le rapport du bundler : je relève chaque<script src>deout/index.html(Next) oudist/index.html(Astro), j'écarte toute balise portantnoModule, et je compresse chaque fichier engzip -9. Le piège est là : si tu cherchessrcavantnoModuledans la balise, tu rates l'attribut et tu comptes 112 594 octets que personne ne télécharge. Ça m'est arrivé au premier passage.

Pour Astro, il faut aussi compter le JavaScript en ligne: il n'apparaît dans aucun fichier et dans aucune requête réseau. C'est tout l'objet de la section précédente.

Ce qu'il faut retenir

Le brief laissait le framework libre et notait le chargement deux fois. J'ai pris Astro. Le choix était bon — mais pas pour la raison qu'on donne d'habitude.

Le gain n'est pas le poids : sur ce site, le code fait 2,4 % de la page et les images tout le reste. Optimiser parfaitement 2,4 % de quelque chose ne rapporte jamais plus que 2,4 %. Si le choix d'Astro a servi à quelque chose ici, c'est ailleurs — dans le travail que le navigateur n'a pas à faire, sur le processeur d'un téléphone d'entrée de gamme. Mon site affiche 0 ms de TBT; je n'ai pas la mesure équivalente côté Next, donc je m'arrête à « c'est là qu'il faudrait regarder » plutôt qu'à « c'est prouvé ».

Ce que la recommandation ne m'avait pas dit, c'est la facture : 171 lignes contre 63, et trois bugs d'accessibilité dedans.

Et il y a une dernière chose, que je n'aurais pas trouvée seul. Les reproches qui sont revenus à la correction ne portaient sur aucun des chiffres de cet article. Ils portaient sur la génération de leads, l'absence de témoignages clients, la conversion. J'ai arbitré une question d'architecture que la grille ne mesurait pas — pendant que ce qu'elle mesurait vraiment attendait.

Ça ne rend pas la mesure inutile. Ça la remet à sa place : elle répond à la question qu'on lui pose, pas à celle qu'il fallait poser.

Et pour l'affirmation par laquelle j'ai commencé — « les Server Components n'envoient presque plus de JavaScript » — la réponse tient en un chiffre : 132 955 octets pour une page qui ne fait rien. Ce n'est pas un détail de mesure. C'est l'écart entre ce qu'une technologie promet et ce qu'elle livre, et il suffisait d'unnpm run buildpour le voir.

Mesure ton propre goulot avant d'appliquer le conseil de quelqu'un d'autre. Y compris le mien.

Toutes les mesures ont été prises le 25 août 2026 : Astro 7.1.3, Next 16.3.2, React 19.2.8, Node 22, builds statiques, même contenu et même fichier de données des deux côtés. Le bundlenoModulede Next est exclu, le build Next est donné avec Turbopack et Webpack. Le site mesuré est BenDjo, et le projet est détaillé sur mon portfolio. Si tu rejoues et que tu trouves autre chose, écris-moi.

Lire l'article sur sahelstack.tech →