SEO JavaScript : comment Google indexe les SPA et les sites React
La file de rendu en deux vagues de Google, les pièges du CSR, la différence entre SSR, SSG et ISR, et des méthodes concrètes pour vérifier que votre site JavaScript est correctement indexé.
Comment Google rend le JavaScript : fonctionnement
Pour une page HTML classique, Google ne fait qu’une étape : il télécharge le HTML et voit immédiatement le contenu. Pour une page JavaScript, il en faut trois : télécharger le HTML, charger le bundle JS, puis l’exécuter dans un Chromium sans interface graphique (headless) pour obtenir le DOM final.
Google utilise son propre moteur de navigation basé sur Chromium, mis à jour régulièrement. Les fonctionnalités JavaScript modernes (ES2020+, import dynamique, WebAssembly) sont donc comprises par Googlebot. Le problème ne vient pas de la prise en charge de la syntaxe, mais du délai de rendu et des contraintes de ressources pendant le crawl.
Le rendu en chiffres
Délai de rendu
Si le JS ne s’exécute pas en environ 3 à 4 secondes, Google indexe une page partiellement rendue
Temps CSS
Un chargement de ressources supérieur à 150 ms peut bloquer le rendu et dégrader l’efficacité du crawl
Indexation
Le contenu JS arrive dans l’index avec un délai par rapport au contenu HTML : parfois de quelques jours, parfois de quelques semaines
Google evergreen
Googlebot se met à jour avec la dernière version stable de Chromium : les fonctionnalités JS modernes sont entièrement prises en charge
Indexation en deux vagues : pourquoi le contenu JS arrive plus tard dans l’index
Google découpe l’indexation en deux étapes. La première vague est le crawl : Googlebot télécharge le HTML et tous les liens découverts. La seconde vague est le rendu : le robot met la page en file d’attente, exécute le JS et indexe le DOM final.
Entre ces deux vagues, il peut s’écouler de quelques heures à plusieurs semaines, selon l’autorité du site, le budget de crawl et la taille de la file de rendu de Google. Si vos textes clés, vos H1, vos balises meta ou vos liens ne sont générés que par JS, ils arriveront dans l’index avec un délai.
Le CSR, principal piège du SEO
Le CSR (Client-Side Rendering) consiste à envoyer un <div id="root"></div> vide depuis le serveur, puis à construire tout le HTML dans le navigateur une fois le bundle JS chargé. React, Vue et Angular en mode SPA fonctionnent ainsi par défaut.
Du point de vue du SEO, le CSR pose problème pour plusieurs raisons : 1) le rendu n’intervient qu’en seconde vague ; 2) les ressources bloquées par le crawler (scripts tiers, CDN soumis à robots.txt) ne se chargent pas, et la page s’affiche de façon incomplète ; 3) toute erreur JS produit une page vide dans l’index.
<!-- CSR: what Google sees in the first wave -->
<html>
<head>
<title></title> <!-- empty -->
</head>
<body>
<div id="root"></div> <!-- no content -->
<script src="/bundle.js"></script>
</body>
</html><title>, la <meta name="description"> et le <h1> uniquement en JavaScript côté client. Google reçoit alors des valeurs vides ou par défaut lors du premier crawl, et indexe la page sans elles.SSR, SSG et ISR : lequel choisir pour le SEO
La solution aux problèmes du CSR tient à la génération du HTML côté serveur. Il existe trois approches principales, chacune optimale dans son propre scénario.
Comparaison des approches de rendu
| Approche | HTML sur le serveur | SEO | Données dynamiques |
|---|---|---|---|
| CSR | Non | Faible | Complètes |
| SSR | Oui (à chaque requête) | Excellent | Élevées |
| SSG | Oui (au build) | Excellent | Non (reconstruction nécessaire) |
| ISR | Oui (à intervalle programmé) | Excellent | Via la revalidation |
| Approche | Idéal pour |
|---|---|
| CSR | SPA authentifiées, tableaux de bord |
| SSR | Pages personnalisées, catalogues |
| SSG | Blog, pages d’atterrissage, documentation |
| ISR | Catalogues de prix, actualités |
SSR (Server-Side Rendering) : le HTML est généré sur le serveur à chaque requête. Les données sont à jour au maximum, mais la charge serveur est plus élevée. Dans Next.js, generateMetadata et les composants async de l’App Router assurent le SSR par défaut.
SSG (Static Site Generation) : le HTML est généré une seule fois au moment du build et servi comme fichier statique. Le TTFB est le plus court, ce qui est idéal pour le SEO. Inconvénient : toute modification des données impose une reconstruction.
ISR (Incremental Static Regeneration) : une formule hybride. Les pages sont construites de façon statique, puis, après une période de revalidation (revalidate: 3600), le serveur les régénère en arrière-plan à la requête suivante. Dans Next.js, c’est l’approche standard pour la plupart des pages.
// Next.js App Router — SSG + ISR
export const revalidate = 3600; // revalidate every hour
export async function generateMetadata(): Promise<Metadata> {
return {
title: 'Product Catalogue',
description: 'Full range with up-to-date prices',
};
}
// All critical content is server-rendered:
export default async function CatalogPage() {
const products = await fetchProducts(); // server fetch
return (
<main>
<h1>Product Catalogue</h1>
{products.map(p => <ProductCard key={p.id} {...p} />)}
</main>
);
}Liens dynamiques et navigation
Googlebot ne comprend que les liens au format <a href="/url">. Les liens implémentés via onClick, button, data-href ou window.location sont invisibles pour le crawler. Les pages accessibles uniquement par ce type de navigation risquent donc de ne pas être indexées.
- Utilisez
<a href>pour tous les liens de navigation, et non unbutton onClickavecrouter.push() - Pagination par paramètres d’URL (
?page=2) : Googlebot crawle ces URL si elles figurent dans le HTML - Routeur SPA : les composants
<Link>de Next.js et de React Router génèrent de vraies balises<a>côté serveur - Navigation par ancre (
#ancre) : elle ne crée pas d’URL distinctes dans l’index. Ne l’utilisez pas pour un contenu qui doit être positionné de manière indépendante - Menus dynamiques : si les éléments de menu s’affichent depuis une API après le chargement de la page, Google peut ne pas les découvrir avant le rendu
Lazy load et défilement infini
Les images en loading="lazy" sont chargées par Googlebot : le crawler moderne prend en charge le lazy loading. Mais une condition s’impose : l’image doit figurer dans le DOM avec un attribut src correct dès le SSR. Si le src est défini par JavaScript dans le navigateur, le crawler voit une balise vide.
Le défilement infini nécessite un chemin explorable vers chaque élément pertinent. Proposez des URL séquentielles et des liens classiques qui fonctionnent sans clic sur un bouton, puis vérifiez avec l’Inspection d’URL ou un crawl que le HTML rendu expose bien le contenu voulu.
| Motif | Vu par Googlebot ? | Recommandation |
|---|---|---|
| <img src="/img.webp" loading="lazy"> | Oui | À utiliser |
| <img data-src="/img.webp"> (JS définit le src) | Non | À éviter |
| Défilement infini sans URL | Premier écran seulement | Ajouter une pagination |
| Contenu derrière un bouton « Afficher plus » | Non | Afficher une partie dans le HTML |
| Contenu en onglets (onglets masqués) | Oui, avec délai | Dupliquer dans le HTML |
Diagnostic : comment vérifier le rendu JavaScript
La question de diagnostic clé est : « Que voit Googlebot sur ma page ? » Plusieurs outils permettent d’y répondre.
Outils de diagnostic
- Google Search Console → Inspection d’URL : affiche une capture d’écran de la page après rendu par Googlebot, ainsi que la liste des ressources et des liens découverts. C’est la méthode de vérification la plus directe.
- « View as Googlebot » dans la GSC : lance le crawler réel et renvoie le HTML après rendu. Comparez-le avec le HTML source.
curlouwget: récupèrent le HTML brut, sans JS. Si les balises meta et le contenu clés figurent dans la sortie decurl, ils seront présents dès la première vague d’indexation.- Désactiver JavaScript dans Chrome DevTools (F12 → Ctrl+Maj+P → « Disable JavaScript ») : une façon rapide de voir la page comme le crawler, sans rendu.
- Screaming Frog avec rendu : le paramètre « Spider > Configuration > Rendering > Googlebot » permet de crawler le site avec rendu JS et de voir les liens découverts après rendu.
# Check what Google sees without JS rendering:
curl -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" \
https://example.com/page
# Find all title / meta description / h1 in HTML response:
curl -s https://example.com/page | grep -E '<title>|<meta name="description"|<h1'Checklist du SEO JavaScript
Contrôles clés
- Le title et la meta description figurent dans la réponse HTML du serveur (SSR/SSG), et non générés par JS
- Le H1, les H2 et le contenu principal sont accessibles sans JavaScript
- Les liens de navigation utilisent
<a href>, et nononClickoubutton - Les données structurées (JSON-LD) figurent dans le
<head>de la réponse HTML - Les images ont un
srcstatique dans le HTML ;loading="lazy"seulement pour les images hors LCP - Le fichier robots.txt ne bloque pas les fichiers JS et CSS nécessaires au rendu
- Aucune erreur JS ne bloque le rendu (à vérifier dans la console de Chrome DevTools, sur la page)
- Core Web Vitals : LCP, INP et CLS atteignent les valeurs seuils de Google
- Pagination par paramètres d’URL, et non uniquement par état JS
- URL canoniques (
<link rel="[canonical](/glossary/canonical)">) présentes dans le HTML, et non définies par JS
FAQ
Google peut-il indexer le JavaScript ?
Un site React ou Next.js est-il automatiquement bien indexé ?
Faut-il recourir au prerendering ou au rendu dynamique ?
Puis-je utiliser React.lazy et le code splitting ?
Comment vérifier que mon site est correctement indexé ?
Check it with SpiderHead
Free and local: run the audit on your own machine.



