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.

Prima pubblicazione: 26 giu 2026 · Ultimo aggiornamento: 3 ago 2026 · Advanced
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 — 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 (non createRoot) 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, sessionStorage o 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-snap o 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.
StrategiaContenuto nell’HTML iniziale?Rischio SEOIdeale per
CSR (React puro)NoMassimoDashboard con accesso, app non indicizzate
Pre-renderingSì (a build-time)BassoSiti piccoli, per lo più statici
SSGSì (a build-time)MinimoBlog, documentazione, marketing
SSRSì (per richiesta)BassoContenuti freschi e dinamici
ISR / ibridoBassoContenuti 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:

  • createRoot renderizza React in un nodo DOM da zero — nessun markup esistente previsto. Usalo per app solo CSR.
  • hydrateRoot aggancia React all’HTML che react-dom/server ha già generato, e si aspetta che il primo render del client produca un output identico a quello inviato dal server. Se usi SSR/SSG, vuoi hydrateRoot, non createRoot — chiamare createRoot su 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.
Evidence for this claim hydrateRoot attaches React to HTML previously generated by React on the server; the initial client output should match the server output. Scope: hydration Confidence: high · Verified: hydrateRoot

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. BrowserRouter usa pushState e produce URL puliti e crawlabili (/products). HashRouter produce /#/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 /products non restituisca 404.
  • <Link> renderizza un anchor reale. Il componente <Link> di React Router produce un <a href>, che è crawlabile. La navigazione basata su handler onClick senza 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 del context per SSR, onChangeClientState, prioritizeSeoTags o titleTemplate. Il sollevamento nativo non copre queste funzionalità — usa react-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 / generateMetadata nell’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.

Add an expert note

Pin an expert quote

New person? Create their unclaimed profile at /admin/experts/ → Pin a quote first.