Refondre 938 pages depuis un corpus JSON
Notre propre site. 938 pages rendues au build depuis un corpus JSON versionné, sans base de données ni serveur en production, déployées sur Netlify. Mesure du 8 septembre 2026, production mobile : score performance Lighthouse 95, LCP 2,8 s, TBT 10 ms, CLS 0,001.
Durée
3 mois — premier commit 8 juin 2026, dernier déploiement 8 septembre 2026, 73 commits
Équipe
2 experts
Technologies clés
938
Pages publiées depuis le corpus JSON au 8 septembre 2026 — 941 fichiers moins les 3 fragments d'interface partagés, soit exactement le nombre d'URL du sitemap ce jour-là
95
Score performance Lighthouse mobile en production sur la page d'accueil — 94 puis 95 sur deux mesures consécutives après déploiement
45
Pages corrigées en une seule passe lors du basculement de doctrine d'hébergement — 802 occurrences classées, 119 lignes remplacées, vérification derrière
551
Vignettes de partage composées automatiquement, une par page éditoriale — 1200 × 630, 38 Ko en moyenne, repli sur une image générique si le fichier manque
#Refondre 938 pages depuis un corpus JSON
nehos-groupe.com est un produit du groupe, pas une mission client. Ses 938 pages ne vivent pas dans une base de données : elles tiennent dans des fichiers JSON versionnés, lus au build par un pipeline qui les rend en HTML statique. Mesure du 8 septembre 2026, production mobile : score performance Lighthouse 95, LCP 2,8 s, CLS 0,001.
#Le problème n'était pas d'écrire les pages, c'était de pouvoir les corriger
Un site de 938 URL change d'échelle. À dix pages, vous corrigez à la main. À cent, vous cherchez et remplacez. À neuf cents, une affirmation écrite en juin est reformulée de quinze façons en août, et vous n'avez plus de moyen fiable de savoir où elle se cache.
Nous avons rencontré ce mur sur notre propre site. La première version partait d'une architecture orthodoxe : Next.js 16, Payload CMS 3, PostgreSQL, Docker. Le contenu était rédigé en JSON, importé en base par un script, puis servi depuis cette base. Cela fonctionnait. Deux défauts nous ont fait changer d'avis.
Le premier : une fois importée, une page n'existait plus qu'en base. Plus lisible dans un diff, plus trouvable en une commande, plus relisible par qui n'ouvrait pas l'administration. Indifférent sur un site vitrine de vingt pages ; angle mort sur un corpus de neuf cents pages qui engage la parole du groupe.
Le second, plus brutal : la base et les fichiers ont divergé. Le formulaire de devis de la page d'audit avait été ajouté directement en base. Le jour où le pipeline Payload a été abandonné, il a disparu de la page — pendant que 1 062 boutons du site, dont celui de l'en-tête, continuaient de pointer vers son ancre. Un appel à l'action qui ne mène nulle part, sur le point d'entrée commercial du site.
Nous avons pris la décision inverse de l'usage : supprimer la base, faire des fichiers la source de vérité unique.
#Ce que nous avons construit
Le site n'a plus ni base de données ni serveur Node en production. next.config.ts déclare output: 'export' : le build produit un dossier de fichiers, et rien d'autre. Ce dossier part sur Netlify. C'est l'approche Jamstack dans sa forme la plus stricte, et la stack que nous livrons en développement Next.js et React sur les projets clients.
Entre le corpus et le HTML, un module de 410 lignes parcourt le dossier de contenu, écarte les trois fragments d'interface partagés — en-tête, pied de page, bandeau — et construit un index en mémoire clé par chemin public. Il reste 938 pages : exactement le nombre d'URL du sitemap publié, et d'entrées du manifeste de soumission aux moteurs.
Chaque page traverse trois passes.
La normalisation ramène les champs hétérogènes à une forme unique. Le corpus a été rédigé sur plusieurs mois : le champ faq y est tantôt un objet, tantôt un tableau ; un comparatif tantôt décrit par critères et solutions, tantôt par en-têtes et lignes. Tant que le CMS servait les pages, ces variantes n'atteignaient jamais les gabarits ; en lecture directe, elles font tomber le rendu.
La construction des blocs dérive du JSON une liste de blocs typés : hero, essentiel, fil d'Ariane, statistiques, FAQ, appel à l'action, cartes, formulaire, outil interactif. Nous avons repris la logique de l'ancien script d'import à l'identique : l'objectif n'était pas un rendu équivalent après le changement de source, mais le même rendu, pour que la refonte ne se paie pas en régressions invisibles réparties sur neuf cents pages.
Le nettoyage retire les champs de pilotage éditorial. Next sérialise l'objet de page entier dans le HTML : grilles d'auto-évaluation E-E-A-T, notes anti-duplication et arbitrages internes se retrouvaient lisibles dans le code source. La première version ne traitait que la racine et laissait passer les champs imbriqués — 76 pages exposaient encore leur mécanique de rédaction. Il est désormais récursif.
Les blocs partent enfin dans un routeur qui mappe 18 types vers autant de composants React, tandis que le champ template choisit l'un des 7 gabarits : article, cas client, comparatif, glossaire, pilier, service, cas d'usage.
#Les choix techniques, et pourquoi
Statique plutôt que rendu à la demande. Un site éditorial dont le contenu change par commits n'a aucun besoin d'un serveur. Le statique supprime une classe entière d'incidents d'exécution et rend le coût d'hébergement indépendant du trafic. Le prix à payer est réel : l'optimiseur d'images de Next ne fonctionne plus, les fichiers sont servis tels quels — une photo de 520 Ko affichée dans une vignette de 144 px partait entière. Nous traitons donc les images au build : plafond à 1200 px de large, recompression, conversion en WebP. Quarante-six fichiers, de 7,0 Mo à 3,0 Mo.
Générer les visuels depuis le corpus plutôt que les produire un par un. Toutes les pages émettaient la même image de partage : sur LinkedIn, un article de blog et une fiche service s'affichaient à l'identique. Un script de build compose désormais une vignette 1200 × 630 par page éditoriale — fond de marque, surtitre de section, titre réel de la page en serif à une taille qui s'adapte à sa longueur, nom de domaine. 551 vignettes générées quand le corpus en comptait 915 ; il en compte 938 aujourd'hui. Trente-huit kilo-octets en moyenne, en aplats plutôt qu'en dégradé : le bruit d'un dégradé triplait le poids du JPEG pour une différence invisible à taille d'aperçu. Si le fichier manque, la page retombe sur la vignette générique — aucune page ne peut se retrouver sans aperçu.
Chercher la cause, pas le symptôme. Pendant six semaines, Lighthouse a renvoyé « NO_LCP » sur la page d'accueil, donc aucun score de performance du tout, alors que les pages internes mesuraient 91 et 93. Six hypothèses ont été éliminées par la mesure, pas par intuition : cache du CDN, outillage, canvas décoratif, animation du titre, hero pleine hauteur, HTML pourtant identique octet pour octet. La cause tenait dans une classe CSS. Le carrousel de cas d'usage portait un scroll-snap obligatoire, et la trace brute raconte tout : la piste émet six événements de scroll entre 1325 et 1414 ms, le First Contentful Paint tombe à 1399 ms, les candidats LCP sont invalidés à 1541 puis à 2210 ms. Chrome abandonne la mesure du Largest Contentful Paint dès qu'un défilement lui est notifié — et le scroll-snap recale la piste à chaque changement de mise en page, donc à chaque police qui arrive et à chaque image décodée. Le snap ne s'applique plus qu'après chargement.
Deux autres corrections ont suivi la même logique. Le H1 apparaissait en fondu depuis une opacité nulle : Chrome n'enregistre aucun candidat tant que son plus grand texte est invisible, si bien que l'animation masquait l'élément même qu'il fallait mesurer. Le titre glisse désormais sans fondu. Et trois familles de polices étaient préchargées en parallèle, dont une monospace qui disputait la bande passante à la serif du H1 — celle qui conditionne réellement le LCP. Les préchargements sont passés de cinq à deux.
Mesures en production mobile, prises deux fois après déploiement : 94 puis 95, LCP 3,0 s puis 2,8 s, TBT 20 ms puis 10 ms. Plus tôt, différer les animations décoratives jusqu'au chargement complet avait fait passer le Speed Index de 6,4 s à 1,9 s, avec un FCP à 1,2 s et un CLS à 0,001. Aucun de ces quatre chantiers ne se serait vu sans mesure avant et après — l'exigence que la Méthode Nehos ROI-First IA impose aux projets clients, appliquée ici à nous-mêmes. Notre fiche Core Web Vitals déplie ces métriques.
#Ce qu'un corpus impose en discipline
Un corpus structuré n'est pas gratuit : il déplace le risque. Plus de schéma de base pour refuser une donnée mal formée, donc il faut un garde-fou explicite et exécuté.
Le nôtre est un script de validation lancé avant le build. Il vérifie cinq choses : que chaque JSON se parse — un seul fichier invalide casse le build entier —, qu'aucun artefact de corruption ne traîne, que chaque lien interne pointe vers une page existante, qu'aucune page n'est orpheline ni trop maigre. La deuxième n'a rien de théorique : un remplacement de masse mal calibré avait laissé des grilles tarifaires incohérentes — sur une page, la formule la plus complète s'affichait moins chère que le palier inférieur. Deux autres scripts complètent le dispositif : l'un réaligne les liens dont la cible a changé de chemin, l'autre supprime ceux dont la cible n'a jamais été écrite.
S'y ajoutent 189 tests unitaires sur la mécanique elle-même : unicité des chemins publics, nettoyage récursif des champs internes, URL canoniques, grappes hreflang, résolution des vignettes, validation des formulaires.
#Ce que ça prouve, si vous avez le même besoin
Une refonte n'oblige pas à réécrire le contenu. Le passage du CMS au rendu direct n'a pas touché une ligne de rédaction : le contenu était déjà séparé de son rendu, changer de moteur revenait à changer de lecteur. C'est exactement ce que l'on cherche quand on migre un site WordPress vers Next.js — sortir le contenu du gabarit avant de changer de gabarit. Les refontes qui coûtent cher sont celles où cette séparation n'a jamais été faite.
Une décision éditoriale devient applicable en une passe. Quand nous avons acté que Netlify est notre plateforme de livraison — et pas telle alternative que le corpus mentionnait par habitude —, il a fallu reclasser 802 occurrences : le contenu pédagogique garde ses faits, glossaire et comparatifs compris ; les offres qui présentaient une autre plateforme comme la nôtre ont basculé. Résultat : 45 pages modifiées, 119 lignes remplacées, une passe de vérification derrière. Même mécanique quand une revue interne a établi que six études de cas présentées comme des clients réels sous NDA n'existaient pas : chaque bloc a été requalifié en « scénario type », dépouillé de ses indicateurs inventés et repointé vers la page de service concernée. Sur un site où le contenu vit en base, ces chantiers se traitent page par page, et personne ne relit la correction dans un diff.
La performance se corrige à la racine, pas page par page. Parce que les 938 pages passent par les mêmes gabarits, un correctif de LCP profite à toutes d'un coup. C'est l'autre face de la contrainte : la discipline du corpus rend l'échelle utile plutôt que subie.
Une réserve. Cette architecture suppose une équipe à l'aise avec Git : un corpus JSON versionné n'est pas une interface d'administration, et nous ne la recommandons pas là où rédaction et publication reposent sur des profils non techniques sans accompagnement. Le bon arbitrage est alors souvent un CMS headless posé au-dessus d'un rendu statique : même séparation entre contenu et rendu, ergonomie différente.
Si votre site présente les mêmes symptômes — trop de pages pour être corrigé à la main, un CMS qui rend le contenu invisible à la relecture, des scores de performance qui ne bougent pas —, l'audit gratuit est le point d'entrée : nous regardons votre corpus et votre chaîne de publication, pas seulement votre page d'accueil.
Ce que la mesure dit, au 8 septembre 2026
938 pages publiées au 8 septembre 2026 — 941 fichiers JSON moins 3 fragments d'interface, chiffre identique dans le sitemap et dans le manifeste de soumission aux moteurs
Score performance Lighthouse mobile 95 en production (94 puis 95 sur deux passages) — FCP 1,2 s, LCP 2,8 s, TBT 10 ms, CLS 0,001
Six semaines de « NO_LCP » résolues par élimination : la cause était le scroll-snap obligatoire d'un carrousel, qui invalide les candidats LCP à chaque recalage
Speed Index de 6,4 s à 1,9 s en différant les animations décoratives jusqu'au chargement complet de la page
Images de 7,0 Mo à 3,0 Mo — 46 fichiers plafonnés à 1200 px, recompressés et convertis en WebP, corpus repointé
551 vignettes de partage composées automatiquement, une par page éditoriale, avec repli sur une image générique si le fichier manque
45 pages mises à jour en une passe lors du basculement de doctrine d'hébergement — 802 occurrences classées, 119 lignes remplacées
189 tests unitaires sur le pipeline de contenu, plus un script de validation du corpus exécuté avant chaque build
L'essentiel sur cette réalisation
nehos-groupe.com est un produit du groupe, construit pour nous : ce n'est pas une mission client. Le contenu du site ne vit pas dans une base de données, mais dans des fichiers JSON versionnés, lus au build par un pipeline qui les rend en HTML statique. Au 8 septembre 2026, le corpus porte 941 fichiers dont trois fragments d'interface partagés — soit 938 pages.
L'architecture de départ était classique : Next.js 16, Payload CMS 3, PostgreSQL, Docker. Nous l'avons abandonnée parce qu'un contenu en base n'est ni relisible dans un diff, ni cherchable en une commande, ni auditable par quelqu'un qui n'ouvre pas l'administration — et parce que la base et les fichiers avaient déjà divergé.
Le site tourne aujourd'hui en export statique sur Netlify, sans base de données ni serveur Node en production. Un module de 410 lignes normalise le corpus, en dérive des blocs typés et retire les champs de pilotage éditorial avant sérialisation ; 18 types de blocs et 7 gabarits produisent l'ensemble des pages.
Chiffres mesurés au 8 septembre 2026 : score performance Lighthouse mobile 95 (FCP 1,2 s, LCP 2,8 s, TBT 10 ms, CLS 0,001) après correction d'un scroll-snap de carrousel qui empêchait Chrome de mesurer le LCP ; images passées de 7 Mo à 3 Mo en WebP ; 551 vignettes de partage générées automatiquement ; 189 tests unitaires sur le pipeline.
Le même diagnostic sur votre site
Nous regardons votre corpus, votre chaîne de publication et vos mesures de performance page par page — pas seulement votre page d'accueil. Audit gratuit, sans engagement.
Questions fréquentes sur cette réalisation
Parce que le CMS ne résolvait pas le problème que nous avions. Payload et PostgreSQL font très bien leur travail quand plusieurs personnes publient chaque jour et qu'il faut des droits, des brouillons et des aperçus. Notre besoin était autre : relire, auditer et corriger en masse un corpus de neuf cents pages écrit sur plusieurs mois. Une fois le contenu en base, il n'apparaît plus dans un diff, ne se cherche plus en une commande, et une correction transversale se fait page par page. Le déclencheur concret a été une divergence entre la base et les fichiers : le formulaire de devis de la page d'audit avait été ajouté directement en base, et il a disparu quand le pipeline d'import a été abandonné, alors que 1 062 boutons du site pointaient toujours vers lui. Un CMS reste le bon choix pour beaucoup de sites ; il n'était pas le bon pour celui-là.
Oui, et c'est la vraie limite de cette architecture — nous ne la présentons pas autrement. Publier suppose d'éditer un fichier, de le committer et de déclencher un build. Cela convient à une équipe déjà à l'aise avec Git, et cela convient mal à une organisation où la rédaction et la publication reposent sur des profils non techniques sans accompagnement. Pour ce second cas, la bonne architecture est généralement un CMS headless posé au-dessus d'un rendu statique : vous gardez la séparation entre contenu et rendu, qui est le vrai gain, et vous récupérez une interface d'édition. Ce que nous déconseillons, dans tous les cas, c'est de laisser le contenu et le gabarit se mélanger — c'est ce mélange qui rend les refontes chères.
Chrome cesse de chercher un candidat Largest Contentful Paint dès qu'un défilement lui est notifié, parce qu'il considère que l'utilisateur a interagi avec la page. Un carrousel en scroll-snap obligatoire recale sa piste à chaque changement de mise en page : chaque police qui finit de charger, chaque image qui se décode déclenche un recalage, donc un défilement, donc une invalidation. Sur notre page d'accueil, la trace montre six événements de scroll entre 1325 et 1414 ms, un First Contentful Paint à 1399 ms, et des candidats LCP invalidés à 1541 puis 2210 ms. Lighthouse renvoyait « NO_LCP », ce qui blanchit le score de performance entier — six semaines durant, avec six hypothèses éliminées avant la bonne. Le correctif tient en une ligne : le snap ne s'applique qu'une fois la page chargée, et le comportement après chargement est strictement identique.
Le déploiement, avant le site lui-même. Next produit environ sept fichiers de préchargement par page pour accélérer la navigation côté client : sur notre corpus, cela représentait 6 400 fichiers et 118 Mo qui dupliquent le HTML déjà généré. À ce volume, l'enregistrement de déploiement expirait pendant l'envoi — deux mises en ligne ont échoué au bout d'une trentaine de minutes. Un script de nettoyage retire désormais ces fichiers avant l'envoi. La contrepartie est assumée et mesurable : sans eux, la navigation redevient classique au lieu d'être instantanée, ce qui est un compromis acceptable pour un site éditorial. C'est le genre d'arbitrage qui n'apparaît qu'au-delà de quelques centaines de pages, et qu'il vaut mieux rencontrer sur son propre site que sur celui d'un client.
En testant la mécanique de contenu elle-même plutôt que des pages une par une — ce qui serait ingérable sur 938 fichiers. Les tests vérifient des invariants structurels : chaque chemin du corpus est unique, les champs internes réservés à l'édition sont retirés avant le rendu, les canonicals correspondent au chemin réel de chaque page, les grappes hreflang sont réciproques, chaque page référencée par une vignette de partage a bien un fichier généré, et les formulaires de devis restent présents sur les gabarits qui doivent les porter. Une régression sur l'un de ces invariants casse un test avant d'atteindre la production, ce qui est le rôle qu'un schéma de base de données jouerait ailleurs — ici, le corpus JSON n'a que ces tests pour se protéger de lui-même.