SEO per React
React di default esegue il rendering lato client, quindi i crawler vedono un guscio vuoto finché JavaScript non viene eseguito. Ecco come Google elabora effettivamente le app React, quale strategia di rendering scegliere e come risolvere il routing, i metadati e il problema del contenuto duplicato per timeout di rendering.
Lingue
React non è dannoso per la SEO, ma il rendering lato client di default lo è. Di default (CRA, Vite + React), il server invia un guscio vuoto e il browser costruisce la pagina, quindi i crawler non vedono nulla finché JavaScript non viene eseguito. Google può eseguire il rendering di React tramite il suo Web Rendering Service, ma il rendering è in coda, ritardato e può andare in timeout - Gary Illyes ha mostrato timeout di rendering che lasciano pagine solo boilerplate che vengono contrassegnate come duplicati. I contratti di rendering dei crawler AI variano in base al fornitore, quindi il contenuto solo CSR aggiunge un rischio di copertura. La soluzione è la strategia di rendering: SSR o SSG (più facilmente Next.js o Remix) mette il contenuto nell'HTML iniziale, idratato con hydrateRoot (non createRoot) in modo che l'output del server e del client corrisponda esattamente. Quindi usa il routing tramite History API, link reali con <a href>, codici di stato corretti e metadati appropriati alla versione: React 19 solleva <title>/<meta>/<link> nativamente, altrimenti react-helmet-async (mai l'originale react-helmet non mantenuto).
TL;DR — React non è negativo per la SEO — ma il modo in cui la maggior parte delle app React è costruita lo è. Di default, React costruisce la pagina nel browser del visitatore, quindi quando un motore di ricerca recupera per la prima volta il tuo URL ottiene una pagina quasi vuota. Google di solito può colmare le lacune eseguendo il tuo JavaScript, ma è più lento e rischioso che consegnare direttamente HTML finito. La soluzione è rendere le tue pagine su un server o al momento della build — di solito con un framework come Next.js.
Perché React è diverso
La maggior parte dei siti web — un blog WordPress, per esempio — invia al motore di ricerca una pagina completa:
il server costruisce l’HTML e lo invia, titolo compreso. Un’app React standard fa
l’opposto. Il server invia un guscio quasi vuoto (praticamente un <div> vuoto),
e poi JavaScript viene eseguito nel browser per costruire la pagina reale.
Questo è ottimo per esperienze fluide, simili a app. È un problema per la SEO, perché la prima cosa che un crawler scarica è quel guscio vuoto. Il tuo contenuto non è ancora lì — appare solo dopo che JavaScript viene eseguito.
Non può Google semplicemente eseguire il JavaScript?
Sì — Google esegue una versione reale e aggiornata di Chrome dietro le quinte e può eseguire il tuo JavaScript per vedere la pagina finita. Quindi il contenuto React può essere indicizzato. Evidence for this claim Googlebot uses an evergreen Chromium rendering engine and can execute JavaScript. Scope: Google Search; successful execution still depends on accessible resources and application behavior. Confidence: high · Verified: Google: JavaScript SEO basics
Ma ci sono degli inconvenienti:
- È ritardato. Google esegue il rendering più tardi, in un passaggio separato che viene messo in coda. Quindi il tuo contenuto può richiedere più tempo per apparire nei risultati di ricerca.
- Può fallire. Se la tua pagina è lenta a caricare il contenuto, il renderer di Google può arrendersi prima che il contenuto appaia — e indicizzare una pagina quasi vuota.
- Altri crawler variano. Bing gestisce JavaScript in modo meno affidabile, e i fornitori di IA non pubblicano un contratto di rendering condiviso. Qualsiasi crawler che recupera solo l’HTML iniziale vedrà un’app React predefinita come vuota.
La soluzione semplice
Porta il tuo contenuto nell’HTML prima che raggiunga il browser. Due modi:
- Rendering lato server (SSR) — un server costruisce la pagina completa per ogni richiesta.
- Generazione di siti statici (SSG) — le pagine vengono costruite in HTML finito in anticipo. Evidence for this claim React supports server rendering APIs and can be used by frameworks that generate HTML outside the browser. Scope: React server APIs; build-time generation is a framework/build-system capability rather than a React mode by itself. Confidence: high · Verified: React: Server APIs
Il percorso più semplice per entrambi è Next.js, un framework basato su React che fa questo al posto tuo. (Remix è un’altra buona opzione.) Con SSR o SSG, il tuo sito React consegna a crawler una pagina completa — ed è amichevole per la ricerca quanto qualsiasi sito web normale.
Alcune altre cose da sistemare
- Usa URL dall’aspetto normale (
/products), non URL con hash (/#/products) — Google non può indicizzare in modo affidabile quelli con hash. - Rendi i tuoi link link reali (
<a href>), non<div>cliccabili. - Dai a ogni pagina il suo titolo e la sua descrizione che si aggiornano quando la pagina cambia.
Vuoi la versione più approfondita — come funziona realmente il renderer di Google, la trappola del timeout di rendering che crea pagine duplicate, il confronto delle strategie di rendering e come testare ciò che Google vede? Passa alla scheda Avanzate.
TL;DR — Il problema SEO di React non è React — è il rendering lato client di default. CRA e Vite + React forniscono un guscio vuoto e costruiscono il DOM nel browser, quindi l’HTML grezzo che un crawler recupera non ha contenuto. Google può renderizzarlo tramite il Web Rendering Service (Chromium evergreen), ma il rendering è messo in coda separatamente, può essere ritardato e può andare in timeout — Gary Illyes ha documentato timeout di rendering che lasciano pagine solo boilerplate che poi vengono segnalate come duplicati. Bing renderizza JS in modo meno affidabile; il rendering dei crawler AI varia a seconda del provider. La soluzione è la strategia di rendering: SSR o SSG (più facilmente Next.js o Remix) mette il contenuto nell’HTML iniziale — e se stai idratando markup renderizzato lato server, usa
hydrateRoot(noncreateRoot) e tratta qualsiasi disallineamento server/client come un bug, non un avviso da sopprimere. Poi usa il routing dell’History API (non URL con hash) e veri link<a href>. Per i metadati<head>: React 19 solleva<title>/<meta>/<link>nativamente; su React 18 o per esigenze avanzate, usa react-helmet-async (mai l’originale non mantenuto react-helmet). Imposta i corretti codici di stato HTTP. Non c’è alcun bonus di ranking per SSR — rende solo il contenuto indicizzabile in modo affidabile. Per i meccanismi generali di rendering, vedi gli argomenti JavaScript SEO e headless CMS.
Cosa rende React davvero difficile per la SEO
React è una libreria JavaScript basata su componenti, e di default — Create React App,
Vite + React — funziona lato client. Il server restituisce un documento quasi vuoto
(famosamente solo un <div id="root"></div>) più un bundle di JavaScript, e il browser
esegue quel JavaScript per costruire il DOM. Confrontalo con una pagina renderizzata lato server
(WordPress, un’app Rails), dove l’HTML completo — contenuto, intestazioni, link —
arriva nella primissima risposta.
Quindi la domanda che decide tutto è: cosa c’è nell’HTML grezzo prima che qualsiasi JavaScript venga eseguito? Per un’app React di default, la risposta è “quasi niente.” Tasto destro → Visualizza sorgente su un’app CRA e vedrai il guscio, non il contenuto. Questo è esattamente ciò che un crawler ottiene al suo primo fetch.
Per essere precisi su dove risiede effettivamente la responsabilità: la libreria React non è
solo CSR. React DOM include rendering client (createRoot), rendering server (API di streaming
statiche) e API di idratazione — la libreria supporta tutto. Il problema del guscio vuoto
è una proprietà della toolchain di default (Create React App, Vite + React senza
server), che collega solo le API client e niente che renderizzi HTML sul
server. Cambia la toolchain — Next.js, Remix o le API di rendering server di React — e
la stessa libreria fornisce HTML completo nella prima risposta.
Questa è l’applicazione specifica di React del più ampio problema JavaScript SEO — vai lì per i modi di fallimento generali (parità, interazione, stato, tempistica). Qui mi concentrerò su ciò che è specifico di React e su come risolverlo.
Come Google elabora effettivamente un’app React
Google gestisce JavaScript in tre fasi: crawl → render → index. Googlebot recupera l’URL, il DOM renderizzato viene costruito successivamente dal Web Rendering Service (WRS) — una versione evergreen di Chromium, lo stesso motore di Chrome — e poi l’output renderizzato viene indicizzato e i suoi link estratti. Evidence for this claim Google processes JavaScript pages through crawling, rendering, and indexing using its Web Rendering Service. Scope: Google Search rendering behavior. Confidence: high · Verified: Google: JavaScript SEO basics
La sfumatura importante è quando avviene il rendering. Il rendering è intensivo in termini di risorse, quindi è messo in coda separatamente dal crawl iniziale. Martin Splitt ha descritto il flusso semplicemente: “we do an HTTP request, and we get something back … some barebone HTML and all it does is load the JavaScript and run the JavaScript. Then, this HTML … goes into rendering. Rendering runs JavaScript — boom!, a lot of content happens that wasn’t there before.” (traduzione) «facciamo una richiesta HTTP e riceviamo qualcosa … un po’ di HTML scheletrico e tutto ciò che fa è caricare il JavaScript ed eseguire il JavaScript. Poi, questo HTML … entra nel rendering. Il rendering esegue JavaScript — boom!, un sacco di contenuto appare che non c’era prima.» Per una pagina React CSR, il “boom” è l’intera tua pagina — nessuna parte di essa esiste finché non viene eseguito quel passaggio di rendering.
Una precisione da tenere a mente: non dare troppo peso al vecchio modello delle “due ondate di indicizzazione”. Lo stesso Splitt ha fatto marcia indietro, definendo l’ondata “una semplificazione eccessiva.” Il punto pratico non è “esiste una Wave 2 formale con tempistiche definite” — è che il rendering è un passaggio distinto, differibile e fallibile, e la CSR mette il 100% dei tuoi contenuti dalla parte sbagliata di esso.
Altri due fatti sul renderer che colpiscono in particolare le app React:
- È senza stato. Googlebot non conserva
localStorage,sessionStorageo cookie tra i caricamenti delle pagine. Qualsiasi contenuto o routing che dipende dallo stato lato client è invisibile al crawler. - Può arrendersi. Il renderer impone un timeout. Se il tuo contenuto principale si carica lentamente — bundle di grandi dimensioni, cascate di chiamate API — il rendering può terminare prima che il tuo contenuto arrivi, e Google indicizza la pagina incompleta.
La trappola del timeout di rendering (e perché crea duplicati)
Questa modalità di errore è raramente spiegata bene, ed è quella più dannosa per le app React. Gary Illyes l’ha descritta direttamente: “I have a bunch of emails in my inbox where the issue is that the centerpiece took forever to load, so rendering timed out … and we were left with a bunch of pages that only had the boilerplate. With only the boilerplate, those pages are dups.” (traduzione) «Ho un mucchio di email nella mia casella di posta dove il problema è che l’elemento centrale ha impiegato un’eternità a caricarsi, quindi il rendering è andato in timeout … e siamo rimasti con un mucchio di pagine che avevano solo il boilerplate. Con solo il boilerplate, quelle pagine sono duplicati.»
Analizza cosa significa per un’app React con CSR. La tua intestazione, la navigazione e il piè di pagina sono boilerplate che si carica velocemente. Il contenuto effettivo della pagina — la parte che rende unico ogni URL — viene recuperato e renderizzato da JavaScript, e si carica lentamente. Il rendering va in timeout. Google rimane con intestazione + navigazione + piè di pagina su ogni URL. Ora ogni pagina appare identica, e Google le segna come duplicati l’una dell’altra in Search Console.
La soluzione di Illyes è la parte pratica: “Try to restructure the js calls such that the content (including marginal boilerplate) loads first and see if that helps.” (traduzione) «Prova a ristrutturare le chiamate js in modo che il contenuto (incluso il boilerplate marginale) si carichi per primo e vedi se aiuta.» Ma la risposta più durevole è non dipendere affatto dal passaggio di rendering per il tuo contenuto principale — il che significa SSR o SSG.
Strategie di rendering per React
Questa è la decisione singolarmente più impattante. Le opzioni, approssimativamente dalla peggiore alla migliore per la SEO:
- CSR (React predefinito). Il server invia lo shell; il browser costruisce tutto. Il contenuto è ritardato dalla coda di rendering ed esposto al timeout. Peggiore per la SEO. Va bene per dashboard autenticate che non vuoi comunque indicizzare. Evidence for this claim Client-only React rendering constructs UI in the browser; server rendering APIs produce HTML before browser hydration. Scope: React rendering mechanics; SEO impact depends on what the initial response contains. Confidence: high · Verified: React: hydrateRoot React: Server APIs
- Pre-rendering. Rendering a build-time senza un framework SSR completo — strumenti come
react-snapo un servizio di prerendering eseguono la scansione della tua app e salvano HTML statico. Più leggero; funziona per siti più semplici e per lo più statici. - SSG (Static Site Generation). HTML costruito una volta al momento del deploy e servito come file statici. Il più veloce, il contenuto è sempre presente nell’HTML grezzo. Limitato per contenuti altamente dinamici o per utente; i siti grandi hanno build lente.
- SSR (Server-Side Rendering). Il server esegue React per ogni richiesta e invia HTML completo. Il contenuto è immediatamente disponibile per i crawler; sempre fresco. Richiede un server Node.js e un TTFB leggermente più alto.
- Hybrid / ISR (Incremental Static Regeneration). Una funzionalità di Next.js che rigenera pagine statiche in background — velocità statica con freschezza periodica.
| Strategia | Contenuto nell’HTML iniziale? | Rischio SEO | Ideale per |
|---|---|---|---|
| CSR (React puro) | No | Massimo | Dashboard con accesso, app non indicizzate |
| Pre-rendering | Sì (a build-time) | Basso | Siti piccoli, per lo più statici |
| SSG | Sì (a build-time) | Minimo | Blog, documentazione, marketing |
| SSR | Sì (per richiesta) | Basso | Contenuti freschi e dinamici |
| ISR / ibrido | Sì | Basso | Contenuti che cambiano ogni ora/giorno |
E una strategia da evitare nelle nuove build: rendering dinamico — rilevare lo user-agent del crawler e servirgli una versione pre-renderizzata mentre gli utenti ricevono CSR. Google ora lo definisce “una soluzione alternativa e non una soluzione a lungo termine” che “crea complessità aggiuntive e requisiti di risorse,” e raccomanda server-side rendering, static rendering o hydration come alternative. (Bing raccomandava il rendering dinamico nel 2018, ma quella guida è datata — dal 2019 Bingbot renderizza tramite Microsoft Edge / Chromium, e SSR/SSG è la scelta giusta anche lì.)
C’è un mito da sfatare qui: SSR non è un boost per il ranking. Come ha detto John Mueller, “non ci sono bonus di ranking SEO per implementarlo in un modo o nell’altro” — i diversi metodi di rendering sono “solo modi diversi di rendere il contenuto indicizzabile.” Il valore di SSR è l’indicizzabilità affidabile (e spesso migliori Core Web Vitals grazie a una First Contentful Paint più veloce), non una leva magica per il ranking.
L’hydration deve corrispondere esattamente — questo è un confine di bug, non una tecnica SEO
Sia SSR che SSG consegnano al browser HTML che contiene già il tuo contenuto. React deve quindi agganciarsi a quel markup sul client, e questa è un’API diversa da un render client semplice:
createRootrenderizza React in un nodo DOM da zero — nessun markup esistente previsto. Usalo per app solo CSR.hydrateRootaggancia React all’HTML chereact-dom/serverha già generato, e si aspetta che il primo render del client produca un output identico a quello inviato dal server. Se usi SSR/SSG, vuoihydrateRoot, noncreateRoot— chiamarecreateRootsu markup renderizzato dal server significa che React lo scarta e ri-renderizza da zero, buttando via il beneficio SEO esatto per cui hai configurato SSR/SSG.
Le discrepanze tra output server e client sono un rischio reale nelle app React che fanno fix SEO — un Date.now() in un titolo, un formato dipendente dalla locale, un ramo if (typeof window !== 'undefined'). La documentazione di React è schietta su cosa succede allora: avvisa sulle discrepanze in sviluppo, ma “non ci sono garanzie che le differenze negli attributi vengano corrette in caso di discrepanze.” La guida è trattare le discrepanze come bug e risolverle — non sopprimere l’avviso e assumere la parità dei contenuti. Per la SEO in particolare: non dare per scontato che il tuo contenuto renderizzato e i metadati corrispondano a ciò che il server ha inviato solo perché la pagina sembra corretta nel browser. Confronta direttamente l’HTML del server con il DOM post-hydration (il controllo View Source vs. Inspect Element della sezione di test qui sotto è la versione veloce di questo) piuttosto che fidarti di una console pulita.
React Router e struttura degli URL
React Router gestisce la navigazione nel browser senza round-trip al server, il che va bene per la SEO se configurato correttamente:
- Usa la History API, non il routing con hash.
BrowserRouterusapushStatee produce URL puliti e crawlabili (/products).HashRouterproduce/#/products, e Google non può risolvere in modo affidabile gli URL basati su hash — il vecchio schema AJAX-crawling che li rendeva funzionanti è deprecato. Usa la History API. - Il server deve gestire anche quegli URL. Con il routing tramite History API, ogni “pagina” ha bisogno di un URL reale a cui il server possa rispondere — fondamentale per SSR, e necessario affinché un accesso diretto o un refresh su
/productsnon restituisca 404. <Link>renderizza un anchor reale. Il componente<Link>di React Router produce un<a href>, che è crawlabile. La navigazione basata su handleronClicksenza anchor non è crawlabile — Google segue solo link<a href>reali.
Gestione dei metadati: react-helmet, react-helmet-async e i tag nativi di React 19
Con React 18, React non ha mai aggiornato nativamente il <head> del documento ai cambi di rotta —
ogni <title>, meta description, canonical e tag Open Graph / Twitter di ogni rotta
dovevano essere impostati da una libreria. React 19 ha cambiato tutto: i componenti possono renderizzare tag <title>,
<meta> e <link> direttamente, e React li solleva da solo in <head> —
funzionando sia con app solo client, sia con SSR in streaming e Server Components. React 19.2 è
la versione stabile attuale a metà 2026, quindi questo vale ora per qualsiasi app su una versione
corrente di React.
Ciò significa che la risposta giusta dipende dalla tua versione di React e da ciò di cui hai effettivamente bisogno:
- React 19, app standalone, solo tag di base. Renderizza
<title>/<meta>/<link>direttamente nei tuoi componenti — nessuna libreria necessaria. - React 19, ma ti servono
htmlAttributes/bodyAttributes, serializzazione delcontextper SSR,onChangeClientState,prioritizeSeoTagsotitleTemplate. Il sollevamento nativo non copre queste funzionalità — usareact-helmet-async. La sua stessa documentazione lo dice chiaramente: senza queste esigenze specifiche, potresti non aver bisogno del pacchetto su React 19. - React 18 o precedente, app standalone. Il sollevamento nativo non esiste ancora — usa
react-helmet-async. È mantenuto attivamente (major version 3, e rileva la tua versione di React a runtime) e supporta SSR. - L’originale
react-helmet. Non usarlo, su nessuna versione di React. Non è mantenuto — nessuna release dal 2020 — e ha bug noti con il rendering concorrente di React 18. - App Next.js. Usa la Metadata API di Next (l’export
metadata/generateMetadatanell’App Router) indipendentemente dalla versione di React — non aggiungere Helmet, e non affidarti nemmeno al sollevamento nativo dei tag di React. Il framework gestisce il documento in un’app Next.js.
Una regola di affidabilità vale in generale per la SEO JavaScript e si applica a prescindere da quale delle opzioni sopra usi: i metadati a livello HTML battono i metadati iniettati via JS. Un tag canonical iniettato da JavaScript lato client è molto meno affidabile di uno presente nell’HTML renderizzato lato server — un altro argomento a favore di SSR/SSG.
I crawler AI rendono tutto questo urgente
La novità del 2026: il comportamento di rendering per GPTBot (OpenAI), ClaudeBot (Anthropic), PerplexityBot e altri agenti è specifico del provider e della versione. Un’app React CSR consegna a ogni crawler lo stesso guscio vuoto che vede il primo fetch di Googlebot, e la documentazione attuale dei provider non stabilisce un passaggio di rendering condiviso che lo riempia. Con i motori generativi che diventano una superficie di scoperta sempre più grande, SSR/SSG smette di essere una preoccupazione solo per Google: l’HTML grezzo massimizza la copertura senza assumere una limitazione universale dei crawler. (Il tema del CMS headless approfondisce questa realtà dei crawler AI.)
Testare ciò che Google vede davvero
Non fidarti del tuo browser — l’Inspector dei DevTools mostra il DOM renderizzato (post-JavaScript), che è esattamente ciò che un crawler senza JS non vede. Usa gli strumenti giusti:
- View Source vs. Inspect Element. View Source è l’HTML grezzo (ciò che i crawler ricevono prima del JS). Inspect Element è il DOM renderizzato. Se il contenuto è in Inspect ma manca da View Source, dipende da JavaScript.
- URL Inspection Tool (Search Console) — il controllo più autorevole. Esegui un test live e guarda l’HTML renderizzato, lo screenshot e le risorse della pagina / messaggi della console per vedere cosa ha effettivamente renderizzato Google e cosa non è riuscito a caricare.
- Rich Results Test — un controllo rapido dell’HTML renderizzato senza verificare il sito.
- Disabilita JavaScript nei DevTools e ricarica — una simulazione veloce di un crawler che non esegue JS (e un buon proxy per ciò che vedono i crawler AI).
- Rapporto Copertura di Search Console — “Scoperto, attualmente non indicizzato” può segnalare un arretrato nella coda di rendering; gruppi di pagine duplicate possono segnalare la trappola del boilerplate per timeout di rendering.
- Crawler con rendering JS — Ahrefs Site Audit e Screaming Frog (modalità rendering JS) renderizzano le pagine su larga scala così puoi confrontare grezzo vs. renderizzato su tutto il sito.
Next.js e Remix (la risposta pratica)
Se la SEO conta e sei su React CSR puro, migrare a un framework che esegue il rendering sul server di solito è la mossa giusta. Next.js è costruito apposta per questo — SSR e SSG pronti all’uso, ISR, App Router, API Metadata integrata, code splitting automatico e ottimizzazione delle immagini. Remix è l’alternativa basata sugli standard web, costruita su fetch/Request/Response con SSR di default e una solida storia di progressive enhancement. Next.js merita un approfondimento dedicato — qui lo tengo volutamente breve. Il punto per la SEO di React è più ristretto: il framework esiste per spostare i tuoi contenuti fuori dal passaggio di rendering solo nel browser e dentro l’HTML iniziale.
React è buono per la SEO quando tratti il rendering come una decisione architetturale piuttosto che un ripensamento. Scegli SSR o SSG per qualsiasi cosa debba posizionarsi, mantieni link e routing onesti, gestisci i metadati per rotta e lascia che siano gli strumenti di Google — non il tuo browser — a dirti cosa è stato effettivamente renderizzato.
Riepilogo AI
Una sintesi della versione Advanced:
- React non è pessimo per la SEO — il CSR di default lo è. La libreria React supporta
rendering client, server, statico e streaming; è la toolchain CRA/Vite di default (senza
server) che fornisce una shell vuota
<div id="root">e costruisce il DOM nel browser, quindi l’HTML grezzo che un crawler recupera non ha contenuti. - Google può renderizzare React tramite il Web Rendering Service (Chromium evergreen), ma il rendering è accodato separatamente, può essere ritardato ed è senza stato (niente cookie/localStorage/sessionStorage tra i caricamenti).
- La trappola del timeout di rendering: se il contenuto principale carica lentamente, il rendering va in timeout e Google indicizza una pagina solo boilerplate. Su molti URL queste appaiono identiche e vengono segnalate come duplicati (la modalità di errore documentata da Gary Illyes). Soluzione: carica prima il contenuto — o meglio, non dipendere dal passaggio di rendering (SSR/SSG).
- Strategie di rendering, da migliore a peggiore per la SEO: SSG (rischio più basso) ≈ SSR ≈ pre-render > ISR/ibrido > CSR (rischio più alto). Il rendering dinamico è deprecato — Google raccomanda SSR, rendering statico o idratazione.
- Nessun bonus di ranking per SSR — Mueller: “no SEO ranking bonuses for implementing it one way or another.” (traduzione) «nessun bonus di ranking SEO per implementarlo in un modo o nell’altro.» Rende solo i contenuti indicizzabili in modo affidabile.
- L’idratazione è un confine di bug, non una tecnica: le app SSR/SSG si idratano con
hydrateRoot(noncreateRoot), che si aspetta che il primo render del client corrisponda esattamente a quello del server. React avvisa sulle discrepanze in dev ma non garantisce di correggerle — tratta le discrepanze come bug e verifica direttamente il DOM del server rispetto a quello post-idratazione. - React Router: usa la History API (
BrowserRouter), non il routing con hash; il server deve gestire quegli URL;<Link>renderizza<a href>crawlable — la navigazione soloonClickno. - Metadati: React 19 solleva nativamente
<title>/<meta>/<link>in<head>per app standalone che necessitano solo delle basi. Su React 18, o per esigenze avanzate (contesto SSR,titleTemplate), usa react-helmet-async — mai l’originale react-helmet, non mantenuto dal 2020. In Next.js, usa l’API Metadata indipendentemente dalla versione di React. Il livello HTML batte quello iniettato via JS. - Il rendering dei crawler AI è specifico del fornitore — il React CSR dipende dall’esecuzione client che ogni crawler può o meno supportare. SSR/SSG mette i contenuti nell’HTML iniziale e massimizza la copertura.
- Testa con View Source vs. Inspect, URL Inspection (HTML renderizzato + screenshot + console), Rich Results Test, ricarica con JS disabilitato e un crawler con rendering JS.
- Next.js / Remix sono la soluzione pratica — spostano i contenuti nell’HTML iniziale.
Documentazione ufficiale
Documentazione di fonte primaria dai motori di ricerca.
- Comprendere le basi della SEO JavaScript — il processo crawl → render → index, le SPA, la History API, gli URL canonici con JavaScript e i codici di stato HTTP significativi.
- Risolvere i problemi JavaScript legati alla ricerca — gestione dei soft-404 nelle SPA, il renderer stateless (senza cookie/localStorage) e i test con URL Inspection.
- Dynamic Rendering (workaround deprecato) — perché Google lo ha deprecato e cosa usare al suo posto (SSR, rendering statico, hydration).
- Introduzione a una nuova serie video sulla SEO JavaScript — la serie di Martin Splitt, che copre specificamente React, Angular e Vue.
- Guida approfondita su come funziona la Ricerca Google — dove si colloca il rendering nel processo crawl → index → serve.
Bing / Microsoft
- Il nuovo Bingbot evergreen (Microsoft Edge) — Bingbot che esegue il rendering di JavaScript tramite la stessa piattaforma Chromium di Googlebot.
- Serie bingbot: JavaScript, Dynamic Rendering e Cloaking — la posizione più datata (2018) di Bing; utile per la raccomandazione storica sul dynamic rendering.
Riferimento tecnico
- React v19 (note di rilascio) — supporto nativo per il rendering dei tag
<title>,<meta>e<link>nei componenti, sollevati automaticamente in<head>. - createRoot / hydrateRoot (documentazione React) — la suddivisione API tra rendering client e hydration, e l’avvertenza che le discrepanze non sono garantite di essere corrette.
- react-helmet-async (npm) — il fork mantenuto per gestire
<head>in app React standalone, per esigenze React 18 o React 19 avanzate.
Citazioni dalla fonte
Dichiarazioni ufficiali del team Search di Google. Ogni collegamento approfondito del motore di ricerca salta al passaggio citato nella pagina di origine; le citazioni del personale di seguito sono collegate alla copertura che le ha riprodotte.
Documentazione Google — SPA e dynamic rendering
- “Single-page applications (SPA) are websites that load an HTML document once and fetch any additional content using JavaScript APIs.” (traduzione) «Le applicazioni a pagina singola (SPA) sono siti web che caricano un documento HTML una volta e recuperano qualsiasi contenuto aggiuntivo utilizzando le API JavaScript.» — Documentazione di Google Search Central. Vai alla citazione
- “Dynamic rendering was a workaround and not a long-term solution for problems with JavaScript-generated content in search engines.” (traduzione) «Il dynamic rendering era un workaround e non una soluzione a lungo termine per i problemi relativi ai contenuti generati da JavaScript nei motori di ricerca.» — Documentazione di Google Search Central. Vai alla citazione
Martin Splitt, Google — come vengono indicizzate le pagine JavaScript
- “What we do is we do an HTTP request, and we get something back, right — some HTML, maybe it’s a barebone HTML and all it does is load the JavaScript and run the JavaScript. Then, this HTML that we got from the original HTTP GET request from the crawl, goes into rendering. Rendering runs JavaScript — boom!, a lot of content happens that wasn’t there before.” (traduzione) «Quello che facciamo è una richiesta HTTP, e riceviamo qualcosa indietro, giusto — un po’ di HTML, magari è un HTML scheletrico e tutto ciò che fa è caricare il JavaScript ed eseguire il JavaScript. Poi, questo HTML che abbiamo ottenuto dalla richiesta HTTP GET originale del crawl, va in rendering. Il rendering esegue JavaScript — boom!, un sacco di contenuto appare che prima non c’era.» Leggi la copertura (Search Engine Journal)
- Sul modello a due ondate impreciso: “there’s no such thing as the second wave of crawling-ish. The wave is an oversimplification.” (traduzione) «non esiste una seconda ondata di crawling. L’ondata è una semplificazione eccessiva.» Leggi la copertura (Search Engine Roundtable)
Gary Illyes, Google — la trappola del contenuto duplicato per timeout di rendering
- “I have a bunch of emails in my inbox where the issue is that the centerpiece took forever to load, so rendering timed out (my most likely explanation) and we were left with a bunch of pages that only had the boilerplate. With only the boilerplate, those pages are dups.” (traduzione) «Ho un sacco di email nella mia casella di posta dove il problema è che il pezzo centrale ha impiegato un’eternità a caricarsi, quindi il rendering è andato in timeout (la mia spiegazione più probabile) e siamo rimasti con un sacco di pagine che avevano solo il boilerplate. Con solo il boilerplate, quelle pagine sono duplicati.»
- “Do you have a JavaScript-heavy site and you see lots of dups reported in Search Console? Try to restructure the js calls such that the content (including marginal boilerplate) loads first and see if that helps.” (traduzione) «Hai un sito pesante in JavaScript e vedi molti duplicati segnalati in Search Console? Prova a ristrutturare le chiamate js in modo che il contenuto (incluso il boilerplate marginale) si carichi per primo e vedi se aiuta.» Leggi il post (LinkedIn)
John Mueller, Google — nessun bonus di ranking per la scelta del rendering
- “There are no SEO ranking bonuses for implementing it one way or another.” They’re “just different ways of making the content indexable (as is client side rendering).” (traduzione) «Non ci sono bonus di ranking SEO per implementarlo in un modo o nell’altro.» Sono “solo modi diversi di rendere il contenuto indicizzabile (come lo è il rendering lato client).” Leggi la copertura (Search Engine Roundtable)
Checklist SEO per React
Un passaggio per confermare che i crawler possano vedere e indicizzare la tua app React:
- Il contenuto importante appare in View Source (HTML grezzo), non solo nel DOM renderizzato — se manca, dipendi dalla CSR.
- Le pagine che devono posizionarsi usano SSR o SSG (Next.js, Remix o un passaggio di pre-rendering), non il rendering lato client grezzo.
- Il contenuto principale si carica velocemente e per primo — niente lente cascate di API che potrebbero far scattare il timeout di rendering in una pagina solo boilerplate.
- Il routing usa la History API (
BrowserRouter), non URLHashRouter/#/. - Il server può rispondere a ogni route lato client (niente 404 su accesso diretto o refresh).
- La navigazione usa veri link
<a href>(React Router<Link>), non handleronClick-only su<div>/<button>. - Ogni route imposta un
<title>unico, meta description, canonical e tag OG che si aggiornano alla navigazione. - I metadati corrispondono alla tua versione: React 19 tag nativi
<title>/<meta>/<link>per le basi, react-helmet-async per React 18 o esigenze avanzate (mai il deprecatoreact-helmet), o la Next.js Metadata API se sei su Next.js. - Se renderizzato lato server, stai facendo l’idratazione con
hydrateRoot(noncreateRoot), e qualsiasi avviso di mismatch di idratazione in modalità dev è trattato come un bug da correggere. - Le route “non trovato” lato client restituiscono un vero 404 (o un
noindex), non un soft-404 con stato200. - JavaScript e CSS non sono bloccati in
robots.txt(Google non renderizza da file bloccati). - Nessun contenuto critico dipende da cookie / localStorage / sessionStorage (il renderer è stateless).
- Verificato in URL Inspection: l’HTML renderizzato e lo screenshot mostrano il tuo contenuto reale.
- Il report di copertura controllato per “Discovered, currently not indexed” (arretrato di rendering) e cluster duplicati (trappola di timeout di rendering).
I modelli mentali
1. L’unica domanda che conta: cosa c’è nell’HTML grezzo? View Source prima che qualsiasi JavaScript venga eseguito è ciò che vede un crawler al primo fetch — e la maggior parte dei crawler AI, per sempre. Se il tuo contenuto non c’è, hai un problema di SEO React indipendentemente da quanto bene la pagina appaia nel tuo browser.
2. Il rendering è un passaggio separato e fallibile. Crawl → render → index. La CSR mette il 100% del tuo contenuto dall’altra parte del passaggio di rendering, che è in coda, ritardato, stateless e può andare in timeout. SSR/SSG spostano il tuo contenuto prima di quel passaggio. Non fidarti troppo del modello “due ondate” — Splitt stesso lo ha definito una semplificazione eccessiva.
3. La modalità di fallimento boilerplate-duplicato. Contenuto lento + timeout di rendering = ogni URL viene renderizzato solo come header/nav/footer = Google vede duplicati. La soluzione è strutturale: carica prima il contenuto, o smetti di dipendere dal passaggio di rendering.
4. L’albero decisionale del rendering.
- Contenuto pubblico che deve posizionarsi o essere citato dall’AI → SSG (statico) o SSR (fresco).
- Per lo più statico (blog, documentazione, marketing) → SSG, o ISR su timer.
- Cambia frequentemente, deve essere fresco → SSR.
- Dashboard con login, non destinata all’indicizzazione → La CSR va bene.
- Nuovo build che necessita di SEO → punta a Next.js / Remix, non al rendering dinamico.
5. HTML-first, JS-second per ogni segnale SEO. Contenuto, link, canonical, titoli, dati strutturati — mettili nell’HTML renderizzato lato server. Tratta i segnali SEO iniettati via JS (inclusi i tag canonical JS e react-helmet su CSR) come fallback, non come piano: Google li vede tardi, e i crawler AI per niente.
React SEO — cheat sheet
Modalità di rendering a colpo d’occhio
| Modalità | Contenuto nell’HTML iniziale? | Rischio SEO | Uso per |
|---|---|---|---|
| CSR (React grezzo) | No | Massimo | Dashboard con login, app non indicizzate |
| Pre-rendering (react-snap) | Sì (al build) | Basso | Siti piccoli, per lo più statici |
| SSG | Sì (al build) | Minimo | Blog, documentazione, marketing |
| SSR | Sì (per richiesta) | Basso | Contenuto fresco e dinamico |
| ISR / ibrido (Next.js) | Sì | Basso | Contenuto orario/giornaliero |
| Rendering dinamico | Solo bot | Deprecato | Non usarlo — usa SSR/SSG/idratazione |
Errori comuni di SEO React → correzioni
| Errore | Correzione |
|---|---|
| Contenuto solo nel DOM renderizzato (CSR) | SSR / SSG / pre-rendering |
URL con hash (/#/path) | History API (BrowserRouter) |
Navigazione con onClick, senza ancora | Vero <a href> / React Router <Link> |
| I meta tag non si aggiornano al cambio di rotta | React 19: tag nativi <title>/<meta>/<link>. Versioni precedenti/avanzate: react-helmet-async. Next.js: Metadata API |
react-helmet (originale, qualsiasi versione di React) | Passa a react-helmet-async o ai tag nativi di React 19 |
createRoot usato su HTML renderizzato lato server | Usa hydrateRoot — createRoot scarta il markup del server |
| Avviso di mismatch di idratazione soppresso | Trattalo come un bug e correggi la differenza server/client |
| Contenuto lento → timeout di rendering → duplicati | Carica prima il contenuto principale; passa a SSR/SSG |
404 lato client restituisce 200 | Vero stato 404 o noindex |
.js / .css bloccati in robots.txt | Consentili — Google non renderizza i file bloccati |
| Contenuto condizionato da cookie/localStorage | Non farlo — il renderer è stateless |
Regole rapide
- Il renderer è Chromium evergreen, in coda, stateless e va in timeout.
- Nessun bonus di ranking per SSR — si tratta di indicizzabilità affidabile (Mueller).
- Il rendering dei crawler AI varia in base al provider → l’HTML grezzo è la baseline di copertura più sicura.
- In Next.js, usa la Metadata API — non React Helmet.
- Bing renderizza JS (tramite Edge) ma in modo meno affidabile; anche lì SSR/SSG è la scelta sicura.
Controlla cosa invia il server prima che React venga eseguito
Inserisci rotte indicizzabili rappresentative in urls.txt. Questo individua shell CSR e markup head mancante nel rendering server-side nella risposta grezza:
while IFS= read -r url; do
html=$(mktemp)
status=$(curl -sS -o "$html" -w '%{http_code}' "$url")
bytes=$(wc -c < "$html" | tr -d ' ')
title_count=$(grep -Eio '<title>[^<]*</title>' "$html" | wc -l | tr -d ' ')
canonical_count=$(grep -Eio '<link[^>]+rel=["'"']canonical["'"'][^>]*>' "$html" | wc -l | tr -d ' ')
printf '%s\t%s\tbytes=%s\ttitles=%s\tcanonicals=%s\n' "$status" "$url" "$bytes" "$title_count" "$canonical_count"
rm -f "$html"
done < urls.txtUna dimensione in byte ridotta è solo un segnale di revisione, non un errore di per sé. Confronta le risposte grezze segnalate con l’HTML renderizzato e conferma che il testo primario e i link crawlabili siano presenti.
Strumenti per l’audit SEO di React
- View Source vs. Inspect Element — il primo controllo più veloce. View Source è l’HTML grezzo (ciò che un crawler riceve prima del JS); Inspect Element è il DOM renderizzato. Il contenuto presente in Inspect ma non in View Source dipende da JavaScript.
- URL Inspection (Google Search Console) — la fonte di verità. Esegui un live test, poi visualizza l’HTML renderizzato, lo screenshot, le risorse della pagina (cosa è stato caricato vs. bloccato) e i messaggi della console per vedere esattamente cosa ha renderizzato Google.
- Rich Results Test — un controllo rapido di HTML renderizzato e dati strutturati per un singolo URL senza verificare il sito.
- Chrome DevTools — disabilita JavaScript (Command Menu → “Disable JavaScript”) e ricarica per ispezionare ciò che un fetcher solo HTML riceve. Questo è un controllo di copertura, non una prova del comportamento di rendering attuale di un crawler AI specifico.
- Rapporto Copertura di Search Console — osserva “Scoperto, attualmente non indicizzato” (arretrato di rendering) e i cluster duplicati (la trappola del boilerplate da timeout di rendering).
- Crawler con rendering JS — Ahrefs Site Audit e Screaming Frog SEO Spider (modalità rendering JS) eseguono JavaScript così puoi confrontare HTML grezzo vs. renderizzato su tutto il sito.
Errori che i team React commettono davvero
Pattern concreti che vedo continuamente nelle app React CSR che vengono pubblicate, non ipotetici. Ognuno è una mossa preventiva — intercettalo prima che ti costi l’indicizzazione.
Pubblicare CRA/Vite CSR grezzo per pagine che devono posizionarsi
I team pubblicano Create React App o Vite + React direttamente in produzione per pagine marketing,
post di blog o pagine prodotto — esattamente il contenuto che deve apparire nella ricerca.
Perché è sbagliato: il server invia un guscio quasi vuoto <div id="root">; il tuo vero
contenuto esiste solo dopo l’esecuzione di JavaScript, quindi è ritardato dalla coda di rendering di Google e
può essere invisibile a qualsiasi crawler AI che recupera l’HTML iniziale senza esecuzione client.
Cosa fare invece: sposta
qualsiasi cosa che debba posizionarsi o essere citata a SSR o SSG — Next.js o Remix sono i percorsi
più semplici — e riserva il CSR grezzo per superfici non indicizzate e riservate agli utenti loggati, come le dashboard.
Routing su URL con hash (HashRouter)
Ricorrere al HashRouter di React Router perché è la via di minor resistenza —
nessuna configurazione del server necessaria, funziona su qualsiasi host statico. Perché è sbagliato: Google non può
risolvere in modo affidabile URL del tipo /#/products; il vecchio schema di crawling AJAX che rendeva
indicizzabili i frammenti hash è deprecato. Cosa fare invece: usa BrowserRouter (la
History API) e assicurati che il server risponda a ogni rotta che produce, incluso un
accesso diretto o un refresh su un deep link.
Costruire la navigazione su onClick invece che su veri anchor
Collegare la navigazione con handler onClick su un <div> o <button>, spesso perché
era più facile da stilizzare o per evitare il comportamento predefinito dei link. Perché è sbagliato: Google segue
solo veri link <a href> — un <div> con un handler di click è invisibile al crawling,
non importa come si comporta per un mouse. Cosa fare invece: usa il componente
<Link> di React Router, che renderizza un vero <a href> sotto il cofano, oppure un anchor
tag semplice per la navigazione esterna.
Caricare il contenuto principale dietro una lenta cascata di API
Recuperare header e nav velocemente, poi concatenare diverse chiamate API prima che il contenuto effettivo della pagina — la parte che rende unico ogni URL — appaia. Perché è sbagliato: il Web Rendering Service di Google impone un timeout; se il tuo contenuto principale si carica lentamente, il rendering finisce prima che arrivi, e Google resta a indicizzare pagine solo boilerplate che poi vengono segnalate come duplicati l’una dell’altra — esattamente la modalità di errore che Gary Illyes ha descritto. Cosa fare invece: ristruttura le richieste in modo che il contenuto principale si carichi per primo, oppure elimina la dipendenza dal rendering lato client con SSR/SSG.
Usare ancora il react-helmet originale
Ricorrere a react-helmet per <title> e meta tag per rotta perché è la
libreria che ogni vecchio tutorial consiglia. Perché è sbagliato: il pacchetto originale non è
mantenuto — nessuna release dal 2020 — e ha problemi noti con il rendering concorrente di React 18
e con SSR. Cosa fare invece: su React 19, renderizza <title>/<meta>/<link>
direttamente nei tuoi componenti e lascia che React li sollevi (nessuna libreria necessaria per le basi).
Su React 18, o per esigenze avanzate come la serializzazione del contesto SSR o titleTemplate, usa
react-helmet-async, il fork mantenuto. Su Next.js, usa la sua Metadata API integrata e
non aggiungere Helmet sopra.
Bloccare JavaScript o CSS in robots.txt
Bloccare /static/js/ o la cartella degli asset di un bundler in robots.txt, a volte residuo
di una vecchia preoccupazione sul crawl budget o copiato dalla configurazione di un altro sito. Perché è
sbagliato: Google non può renderizzare ciò che non gli è permesso recuperare — un bundle bloccato significa che il
Web Rendering Service costruisce un DOM incompleto (o vuoto), anche se il tuo codice sorgente è a posto.
Cosa fare invece: permetti ai crawler di recuperare il tuo JS e CSS, e confermalo
con il controllo delle risorse della pagina dello strumento URL Inspection per vedere che nulla di critico sia bloccato.
Mettiti alla prova: React SEO
Cinque domande rapide per rendere le app React crawlabili e indicizzabili. Scegli una risposta per ciascuna, poi controlla.
Risorse che meritano il tuo tempo
I miei scritti correlati
- React SEO: Best Practices to Make It SEO-Friendly (Ahrefs) — la guida React SEO su Ahrefs, che ho recensito; questo articolo è il trattamento più approfondito e con fonti.
- JavaScript SEO: A Definitive Guide (Ahrefs) — la mia guida completa ai meccanismi di rendering sottostanti: parità DOM, regola della direttiva più restrittiva, gestione di canonical e meta tag, e scelte di rendering. Leggila per il caso generale dietro le correzioni specifiche di React.
- The Beginner’s Guide to Technical SEO (Ahrefs) — dove React/JavaScript SEO si inserisce nel quadro più ampio.
I miei interventi
- How Search Works (SlideShare) — la mia spiegazione di crawling, rendering, indicizzazione e ranking. (Vale la mia consueta precisazione: “This is my understanding of systems… not going to be 100% complete or accurate.” (traduzione) «Questa è la mia comprensione dei sistemi… non sarà completa o accurata al 100%.»)
Dal settore
- Comprendere le basi della SEO JavaScript (Google) — documentazione di fonte primaria su SPA, History API e gestione di canonical/status code.
- Rendering dinamico (deprecato) (Google) — perché il rendering dinamico è una soluzione temporanea, non una soluzione a lungo termine.
- Martin Splitt spiega come vengono indicizzati i siti JavaScript (Search Engine Journal) — la spiegazione “barebone HTML … boom” del flusso crawl → render → index.
- Gary Illyes su siti con molto JavaScript e contenuti duplicati (LinkedIn) — la modalità di errore da timeout di rendering a duplicati, con le sue parole.
- Come risolvere i problemi SEO tecnici nelle app React lato client (Search Engine Land) — un caso di studio reale su audit e correzione di un’app React CSR.
- SSR vs. rendering dinamico — nessuna differenza di ranking (Search Engine Roundtable) — la dichiarazione di Mueller su “nessun bonus SEO di ranking”.
- react-helmet-async (npm) — la libreria di gestione head mantenuta per React standalone.
- Il nuovo evergreen Bingbot (Microsoft Edge) (Bing) — Bingbot che esegue il rendering di JS tramite Chromium, come Googlebot.
Video
- Google Search Central — serie SEO JavaScript (YouTube) — la serie video ufficiale di Martin Splitt copre la SEO per React, Angular e Vue in particolare, illustrando il processo crawl → render → index e le correzioni comuni. Annuncio della serie · Canale
Cronologia modifiche
Aggiornato il 18 lug 2026.
Riepilogo editoriale e dettagli registrati delle modifiche.Dettagli delle modifiche
-
Le note dettagliate sulle modifiche sono attualmente disponibili in inglese.
-
Le note dettagliate sulle modifiche sono attualmente disponibili in inglese.
-
Le note dettagliate sulle modifiche sono attualmente disponibili in inglese.
Confronto completo non disponibile — non è stata archiviata alcuna istantanea precedente per questa revisione.