Une page catégorie affiche vingt-quatre produits, puis charge la suite lorsque le visiteur approche du bas. Le problème apparaît lorsqu’un crawler sans interaction, un utilisateur qui ouvre un nouvel onglet ou un navigateur qui restaure l’historique ne voit jamais le vingt-cinquième produit. Le catalogue existe dans l’API, mais pas dans le parcours adressable.
Pour les leads frontend, responsables SEO e-commerce, product owners et équipes plateforme, le vrai enjeu est de partager un contrat entre navigation classique et expérience enrichie. Ce n’est pas ajouter un bloc de liens caché pour les robots, c’est faire de l’infinite scroll une amélioration progressive. La méthode permet de conserver des pages paginées réelles sous l’interface fluide.
En réalité, Google indique ne pas faire défiler la page ni cliquer sur un bouton pour charger du contenu. Concrètement, chaque lot doit être accessible par une URL persistante et un href fonctionnel. Contre-intuitivement, le « fallback SEO » le plus fiable est l’endpoint que le JavaScript utilise lui-même, pas une seconde navigation décorative.
Une intervention Tech SEO et performance web aligne SSR, API, href, History API, accessibilité et budgets frontend. Elle livre une parité mesurable, une recette de restauration et un rollback avant d’ouvrir le scroll infini à tout le catalogue.
Comprendre ce que le scroll masque
Comparer API, HTML, DOM et historique
Le premier symptôme est une couverture différente selon le mode d’accès : 100 % des produits via API, une fraction dans le HTML initial et une autre fraction après plusieurs intersections. Si la parité reste sous 100 %, alors l’équipe bloque le canari jusqu’à validation par la QA. Le second est une URL qui reste sur la page 1 alors que le visiteur consulte le lot 8 ; l’équipe corrige alors l’historique avant extension. Le troisième est un retour navigateur qui recharge le début et perd le contexte.
Le diagnostic compare source HTML, DOM après hydratation, requêtes réseau, historique et crawl sans JavaScript. Il enregistre les identifiants produits par lot, leurs href, le numéro de page demandé et la canonicale. Un simple screenshot ne révèle ni la couverture ni les duplications.
Le dossier distingue défaut de graphe, erreur d’API, concurrence de requêtes et problème de restauration. Cette séparation empêche de corriger la découverte avec un preload ou la performance avec une canonicale, deux leviers sans rapport avec la cause.
Partager un seul contrat paginé
Donner les mêmes entrées et sorties au SSR et à l’API
La pagination demeure la source de vérité : catégorie, filtres, ordre, numéro ou curseur, taille de lot, résultat, borne et URL suivante. Le SSR rend ce contrat ; l’API l’expose ; le frontend l’enrichit. Aucun canal ne recalcule seul la position ou le nombre de pages.
Le contrat renvoie des identifiants stables, la plage affichée, l’URL persistante du lot et le lien suivant lorsque la suite existe. Une erreur explicite distingue fin de catalogue, paramètre invalide et indisponibilité temporaire. L’interface ne transforme pas un 500 en fin silencieuse.
Si le contrat SSR et l’API produisent le même ensemble et le même ordre, alors le scroll peut intercepter les liens. En revanche, une API fondée sur un tri différent doit être alignée avant enrichissement. L’équipe protège ainsi la sémantique plutôt que de maquiller l’écart côté DOM.
Donner une URL persistante à chaque lot
Rendre le lot copiable et vérifiable
Chaque lot possède une URL telle que ?page=4, directement chargeable, copiable et partageable. Elle ne dépend pas d’un fragment ou d’un token de session. Les filtres autorisés et l’ordre stable font partie de l’identité ; les paramètres de tracking sont normalisés séparément.
Une URL persistante sert trois usages : découverte par href, reprise de navigation et preuve de parité. Elle permet de demander le lot 4 au serveur et de comparer ses identifiants à ceux que le scroll a ajoutés lors du passage de 3 à 4.
Les contenus relatifs au temps — « nouveautés des dernières heures » — ne doivent pas déplacer arbitrairement la frontière pendant une même session. Le contrat peut figer un repère ou utiliser un ordre stable. Sinon recharge et historique ne retrouvent jamais le même contexte.
Servir chaque lot directement en SSR
Aligner contrôleur, requête et clé de cache
Une requête directe sur la page 6 renvoie son titre, sa canonicale propre, ses produits, le lien précédent, le lien suivant et le retour à la première page dans l’HTML initial. Le contenu essentiel ne dépend ni d’une hydratation réussie ni d’une requête lancée après le chargement.
Le contrôleur, le repository et le cache prennent les mêmes arguments que l’API. Le SQL ou le moteur de recherche applique ordre et départage identiques. La clé CDN inclut uniquement les dimensions qui changent réellement le lot. Cette combinaison route + requête + cache rend le rendu direct reproductible.
Si le SSR échoue, alors le statut reflète l’échec et le mode dégradé peut servir une dernière version valide documentée. En revanche, un HTML 200 vide qui attend JavaScript masque l’incident et fausse la recette SEO.
Conserver un vrai lien suivant
Préserver clic natif, clavier et nouvel onglet
Le lot courant contient un élément <a href="?page=2"> ou un bouton enveloppé par une navigation dont le lien reste activable. Sans JavaScript, un clic charge la page suivante. Avec JavaScript, le même href devient l’entrée de l’intercepteur.
Le lien n’est pas remplacé par un div muni d’un gestionnaire. Il peut être ouvert dans un nouvel onglet, copié et parcouru au clavier. Sa destination provient de la réponse serveur plutôt que d’un compteur local susceptible de dériver.
Lorsque la fin est atteinte, le serveur n’envoie plus d’URL suivante et le frontend retire le déclencheur. Il ne conserve pas un lien vers une page vide. Une page au-delà de la borne répond 404 pour fermer le graphe.
Intercepter le lien sans changer sa destination
Rendre Fetch idempotent et annulable
Le gestionnaire annule la navigation seulement lorsque Fetch, History API et le conteneur sont disponibles. Il demande exactement le href, vérifie statut et type de contenu, extrait le lot, déduplique les identifiants, insère le fragment puis met à jour la navigation. En cas d’échec, le lien classique reste utilisable.
L’append est idempotent : rejouer la réponse du lot 3 ne doit pas dupliquer les produits. Un registre des URLs et identifiants chargés protège contre double clic, reprise réseau et intersection répétée. Un AbortController annule la requête obsolète lors d’un changement de filtre.
Plutôt que reconstruire l’URL dans le client, l’intercepteur consomme celle rendue par le serveur. Cette décision évite les divergences de normalisation, conserve les paramètres autorisés et simplifie le replay des logs réseau.
Limiter IntersectionObserver au déclenchement
Borner anticipation et concurrence
IntersectionObserver indique qu’une sentinelle approche du viewport ; il ne prouve ni la découvrabilité ni la réussite du chargement. Le callback déclenche le même flux que le clic sur le lien suivant. Si l’API échoue, l’interface réaffiche ce lien et propose un nouvel essai.
La marge d’anticipation est mesurée selon réseau et taille du lot. Une marge trop grande précharge plusieurs pages que le visiteur ne verra jamais ; une marge trop faible laisse un trou visuel. Le monitoring compare requêtes lancées, lots vus et octets gaspillés.
Une seule requête reste active par flux. Le callback vérifie l’état, désabonne l’ancienne sentinelle et n’observe la nouvelle qu’après insertion. Cette séquence évite les appels concurrents et les lots ajoutés dans le désordre.
Synchroniser l’URL avec History API
Mémoriser lot et ancre sans gonfler l’état
L’URL affichée suit le lot principal visible, pas chaque pixel de scroll. Un seuil stable choisit le segment dominant ; replaceState évite de remplir l’historique à chaque intersection, tandis que pushState peut marquer une action volontaire comme « charger plus » selon le contrat UX.
L’état d’historique contient URL, numéro de lot, ancre produit et position relative. Il ne stocke pas des milliers de fiches. Lors d’un popstate, l’application recharge les lots nécessaires depuis leurs URLs persistantes, puis replace le viewport après rendu.
L’URL visible n’est pas automatiquement la canonicale du document composé. Chaque endpoint direct conserve sa canonicale propre. L’équipe ne modifie pas une balise de tête au fil du scroll en supposant que ce changement client redéfinit la ressource initiale.
Restaurer retour, recharge et position
Rejouer le parcours depuis une URL directe
La recette commence sur la page 5, recharge, revient à la page 4, avance, ouvre une fiche puis retourne à la liste. Le même lot, le même produit d’ancrage et une position proche doivent être retrouvés. La restauration native du navigateur est coordonnée avec le rendu asynchrone pour éviter un saut avant insertion.
Le stockage local peut conserver un cache léger d’identifiants et de hauteurs, mais l’URL reste la source de vérité. Une entrée périmée ne bloque pas le chargement SSR. Le cache est versionné avec tri, filtres et taille de lot afin d’éviter de restaurer un autre catalogue.
Si la référence d’ancrage a disparu, alors le système revient au début du lot et l’annonce. En revanche, il ne renvoie pas silencieusement en page 1, ce qui ferait perdre le contexte et masquerait la mutation.
Préserver contrôle et annonces accessibles
Garder un contrôle manuel et le footer atteignable
Le chargement automatique n’est pas le seul mode. Un contrôle « charger plus » reste activable au clavier, annonce l’état occupé et reçoit le focus lorsque l’erreur exige une action. Une région live signale le nombre de nouveaux éléments sans lire toute la liste.
Le focus ne saute pas automatiquement sur le premier produit ajouté lors d’un scroll passif. Après une activation volontaire, la politique est testée avec lecteurs d’écran. Le footer demeure atteignable : une succession infinie de chargements ne doit pas le repousser sans contrôle.
La préférence de réduction des mouvements et les contraintes de données peuvent désactiver le chargement automatique. L’utilisateur conserve alors la pagination ou le bouton. L’amélioration progressive permet ce choix sans créer un second parcours fonctionnel.
Prouver la parité pagination-scroll
Comparer identifiants, ordre et champs visibles
Pour chaque lot, le test récupère l’endpoint SSR, en extrait les identifiants ordonnés puis déclenche le scroll depuis le lot précédent. Les deux listes doivent être identiques. Il compare aussi href, prix, stock, titre et liens produits lorsque ces champs peuvent diverger.
Un hash de lot facilite le monitoring, mais le diagnostic conserve les identifiants qui diffèrent. La parité est testée à cache froid et chaud, sur plusieurs locales et après mutation catalogue. Une égalité sur un seul jeu figé ne prouve pas la cohérence de la pipeline.
Le test ajoute lenteur réseau, réponse 500, timeout, double intersection et changement de filtre en vol. Il vérifie annulation, reprise, déduplication et URL. Ces scénarios rapprochent le contrôle de la production plutôt que d’un chemin heureux.
Borner DOM, mémoire et requêtes
Définir un budget par session longue
Un scroll infini peut conserver des milliers de nœuds, observateurs, images et gestionnaires. Le budget suit nombre de lots montés, nœuds DOM, mémoire, requêtes, octets, temps de script et INP. La virtualisation peut réduire la charge, mais elle doit préserver focus, dimensions, restauration et liens accessibles.
Les images sous la ligne utilisent dimensions réservées et chargement différé. Les lots déjà chargés ne sont pas redemandés. Les prefetchs respectent réseau et intention. Un cache client possède une limite et une stratégie d’éviction au lieu de croître jusqu’à la fermeture de l’onglet.
Si le budget DOM est dépassé, alors l’équipe bascule vers un bouton ou une pagination visible. En revanche, supprimer brutalement les nœuds précédents sans conserver hauteurs et ancrage casse le retour. L’arbitrage performance inclut toujours la restauration.
Mesurer couverture, expérience et découverte
Séparer parité immédiate et effets différés
La couverture compare produits attendus, SSR direct, crawl HTML et séquence scroll. Les doublons et absences sont comptés par lot. Les logs suivent premier hit, statuts et latence des endpoints. Aucun taux n’est présenté sans volume ni fenêtre.
L’expérience suit erreurs de chargement, reprises, lots effectivement vus, INP, mémoire, DOM, octets et restauration réussie. Les cohortes business comparent clic produit et conversion entre pagination et scroll, sans attribuer toute différence au mécanisme si merchandising ou stock changent.
Une baseline de vingt-huit jours précède la release. Les contrôles techniques s’exécutent à chaque build et à J+1 ; performance et erreurs sont relues à J+7 ; logs et GSC à J+14 ou J+28. Les résultats distinguent faits observés, interprétations et hypothèses.
Décider sur deux scénarios simulés
Arbitrer parité, restauration et INP
Scénario 1 entièrement simulé. Une catégorie fictive contient 9 600 produits. Le scroll charge quarante lots, mais le HTML initial ne lie que le premier. Sur vingt-huit jours simulés, 76 % des fiches profondes ne reçoivent aucun hit via la catégorie et 12 % des sessions qui reviennent depuis une fiche perdent leur position. Si l’absence de hit dépasse le seuil interne de 10 %, alors l’équipe refuse l’extension et répare le graphe paginé.
Scénario 2 entièrement simulé. Les mêmes endpoints deviennent SSR, le lien suivant alimente Fetch et History API, et l’append déduplique les identifiants. Un canari sur dix catégories atteint 100 % de parité sur 240 lots, ramène les échecs de restauration à 0,7 % et limite le DOM à six lots montés. Si la parité reste à 100 % et les échecs sous le seuil de 1 %, alors l’équipe étend le dispositif au palier suivant.
Seuil 1 simulé : zéro absence ou duplication sur 10 000 identifiants de référence. Seuil 2 simulé : moins de 1 % d’erreurs de chargement et de restauration, avec un p75 INP qui ne se dégrade pas de plus de 20 ms. Ces seuils ne prédisent ni crawl ni trafic.
La décision fictive étend le canari tout en conservant un bouton sur les appareils sous contrainte. Si la parité tient mais que la mémoire dépasse le budget, alors l’équipe corrige le fenêtrage avant généralisation.
Recetter erreurs, concurrence et repli
Simuler timeout, double événement et changement de filtre
La matrice couvre accès direct à chaque lot, JavaScript désactivé, clic, intersection, double déclenchement, timeout, 404, 500, changement de filtre et fin du catalogue. Elle vérifie route, statut, canonicale, href, identifiants, ordre, historique, focus et annonce accessible.
Les tests navigateur instrumentent Fetch, AbortController, IntersectionObserver et popstate. Ils simulent réseau lent, cache vide et retour arrière. Le crawl HTML valide séparément le graphe. Le test de charge mesure SSR, API et erreurs hors borne.
- Par exemple, timeout : afficher le href de reprise et conserver le lot courant sans duplication.
- QA et CI : comparer les identifiants SSR, HTML et JavaScript sur chaque route.
- Invalidation : vérifier le cache après changement de filtre et mesurer le TTFB du repli.
- Observation : suivre Googlebot, crawl et indexation sans présenter leur évolution comme garantie.
Prouver instrumentation et retour au fallback
L’instrumentation prend pour entrées href, lot courant, ancre et version de filtre ; ses sorties portent identifiants ajoutés, URL affichée et état de reprise. La journalisation nourrit le monitoring de la parité, des retry et des seuils de mémoire sous la responsabilité du lead frontend.
Le rollback désactive l’interception tout en laissant la pagination fonctionner. Il ne supprime ni les endpoints ni les href. Le runbook indique feature flag, version, owner, métriques de garde, seuil d’arrêt et purge de cache éventuelle.
Une personne extérieure au développement reproduit les scénarios avec les droits de production. Si elle doit connaître un raccourci non documenté, alors la recette n’est pas close.
Choisir quand ne pas utiliser le scroll infini
Comparer exploration continue et accès précis
Le scroll convient à une exploration continue où l’utilisateur compare de nombreux éléments. Il convient moins à une recherche orientée vers un emplacement précis, à un catalogue où le footer est important ou à des appareils fortement contraints. La pagination peut rester la meilleure expérience.
Produit décide le mode ; frontend garantit contrôle, historique et budget ; backend possède les lots ; SEO vérifie graphe et canonicales ; accessibilité teste annonces et focus ; SRE observe erreurs et charge. L’owner n’est pas seulement l’équipe qui dessine la sentinelle.
Sur un petit catalogue, le coût de cette architecture dépasse parfois sa valeur. Plutôt que construire virtualisation et restauration complexes, une pagination rapide avec préchargement mesuré peut être plus robuste.
Éviter les faux fallbacks
Cacher des liens que l’application n’utilise pas
Une navigation invisible ajoutée uniquement pour le crawl diverge tôt ou tard de l’API. Elle peut pointer vers des pages vides, conserver un ordre ancien ou être retirée lors d’une refonte. Le lien visible et fonctionnel doit alimenter le même endpoint que l’expérience enrichie.
La deuxième erreur consiste à considérer IntersectionObserver comme une preuve de découverte. Il ne fait que déclencher du code dans un navigateur compatible. Le crawl HTML et l’accès direct aux URLs restent les preuves.
Mettre à jour l’URL sans restaurer l’état
History API peut afficher ?page=8, mais cette chaîne ne vaut rien si une recharge montre la page 1 ou si le retour perd le produit. URL, lot et ancre forment un même contrat de reprise.
La quatrième erreur est de laisser le DOM croître indéfiniment. Une expérience fluide sur cinq lots peut devenir inutilisable sur cinquante ; le budget et le mode dégradé sont testés avant production.
Plan d’action : livrer en trois semaines
Semaine 1 : prouver le fallback
Le premier jour inventorie endpoints, lots, href, canonicales et différences SSR/API. Le deuxième crawle sans JavaScript et mesure la couverture. Le troisième définit URL, ordre, taille, borne et réponse d’erreur. Le quatrième rend chaque lot directement et ajoute les liens précédente-suivante. Le cinquième exécute parité des identifiants, accès direct et 404 hors borne.
À ce stade, la pagination doit être complète sans aucune interception. Si elle ne l’est pas, alors le scroll reste désactivé. Cette gate empêche le frontend de compenser un contrat backend incomplet.
Semaine 2 : enrichir et restaurer
Le sixième jour intercepte le href avec Fetch et repli natif. Le septième ajoute déduplication, AbortController et concurrence. Le huitième configure IntersectionObserver. Le neuvième synchronise History API, popstate et ancre. Le dixième traite focus, région live, contrôle manuel, limites DOM et mémoire.
Chaque étape possède un test négatif. Une réponse 500 doit rendre le lien de reprise ; un double événement ne doit pas dupliquer ; un retour doit retrouver le contexte. L’équipe versionne le corpus et rejoue le feature flag de rollback.
Semaine 3 : canarier et mesurer
Le canari couvre catégories profondes, catalogue volatil, mobile lent et navigation clavier. Pendant sept jours, le suivi compare parité, erreurs, restauration, INP, mémoire, DOM, octets et conversion. Le crawl HTML contrôle la couverture ; les logs suivent les endpoints. À J+7, l’équipe étend, corrige ou replie ; à J+28, elle relit découverte sans promettre d’effet automatique.
Le rapport archive faits, seuils et arbitrages. Il indique les appareils où le bouton reste préférable et la limite de lots montés. Une future modification de tri, taille ou API doit rejouer le corpus complet.
- Choisir le fallback : si un lot n’est pas rendu directement en SSR, alors refuser l’activation du scroll.
- Corriger le graphe : si un href ne fonctionne pas sans JavaScript, restaurer la route paginée avant l’interception.
- Replier l’enrichissement : si parité, mémoire ou restauration dépassent leur seuil, alors désactiver le feature flag plutôt que masquer l’erreur.
- Étendre le canari seulement si navigation, accessibilité, performance et couverture restent stables.
Relier rendu JavaScript et pagination
Auditer la parité source-DOM avant l’enrichissement
L’audit sur le rendu JavaScript, SSR et hydratation fournit le protocole source-DOM nécessaire pour repérer les liens perdus. Il aide à isoler si le défaut vient du serveur, du bundle ou de l’hydratation.
La méthode consacrée à la pagination et à la profondeur de crawl permet de construire le graphe que le scroll enrichira. Les deux approches partagent les mêmes routes et les mêmes tests de couverture.
Consulter les sources primaires
Cadrer lazy loading, historique et observation
Google précise que Search ne fait pas défiler ou cliquer pour charger la suite dans sa documentation sur le lazy loading JavaScript. Ses recommandations sur la pagination et le chargement incrémental demandent des URL persistantes et liées.
Le standard HTML décrit la navigation et History API, tandis que le W3C définit IntersectionObserver. Ces API permettent l’enrichissement ; elles ne remplacent pas le contrat SSR.
Conclusion : améliorer une pagination déjà complète
Un infinite scroll robuste commence par une pagination complète, directement chargeable et parcourable sans JavaScript.
Le frontend intercepte les mêmes href, déduplique les lots, synchronise l’historique et restaure l’ancre. IntersectionObserver ne sert que de déclencheur.
La parité des identifiants, la couverture HTML, les erreurs, la mémoire et l’INP rendent la décision observable. Un bouton ou une pagination reste disponible lorsque le budget est dépassé.
Pour réunir SSR, navigation, accessibilité et performance dans un contrat testable, l’accompagnement Tech SEO et performance web de Dawap sécurise le fallback avant d’enrichir l’expérience.