Panduan React SEO

React renders client-side oleh default, so crawler see sebuah empty shell until JavaScript runs. Here's how Google actually processes React apps, which rendering strategy untuk pick, dan cara fix routing, metadata, dan render-timeout duplicate-konten trap.

Pertama kali diterbitkan: 26 Jun 2026 · Terakhir diperbarui: 3 Agu 2026 · Advanced
Bahasa

React adalah not buruk untuk SEO — tetapi rendering sisi klien oleh default adalah. Out dari box (CRA, Vite + React), server ships sebuah empty shell dan browser membangun halaman, so crawler see nothing until JavaScript runs. Google dapat render React via -nya Web rendering Service, tetapi rendering adalah queued, delayed, dan dapat time out — Gary Illyes memiliki ditampilkan render timeouts leaving boilerplate-hanya halaman itu get marked sebagai duplicates. AI-crawler rendering contracts vary oleh provider, so CSR-hanya konten menambahkan sebuah coverage risk. fix adalah rendering strategy: SSR atau SSG (sebagian besar easily Next.js atau Remix) puts konten di initial HTML, hydrated dengan hydrateRoot (not createRoot) so server dan client output match exactly. lalu gunakan History API routing, nyata <a href> tautan, correct kode status, dan versi-appropriate metadata: React 19 hoists <title>/<meta>/<link> natively, otherwise react-helmet-async (tidak pernah unmaintained original react-helmet).

TL;DR — React’s SEO masalah isn’t React — ini adalah rendering sisi klien oleh default. CRA dan Vite + React ship sebuah empty shell dan bangun DOM di browser, so raw HTML sebuah crawler fetches memiliki tanpa konten. Google dapat render ini via Web rendering Service (evergreen Chromium), tetapi rendering adalah queued separately, dapat menjadi delayed, dan dapat time out — Gary Illyes memiliki documented render timeouts leaving boilerplate-hanya halaman itu lalu get flagged sebagai duplicates. Bing renders JS less reliably; AI-crawler rendering varies oleh provider. fix adalah rendering strategy: SSR atau SSG (sebagian besar easily Next.js atau Remix) puts konten di initial HTML — dan jika Anda’re hydrating server-rendered markup, gunakan hydrateRoot (not createRoot) dan treat apa pun server/client mismatch sebagai sebuah bug, not sebuah warning untuk suppress. lalu gunakan History API routing (not hash URLs) dan nyata <a href> tautan. untuk <head> metadata: React 19 hoists <title>/<meta>/<link> natively; pada React 18 atau untuk advanced perlu, gunakan react-helmet-async (tidak pernah unmaintained original react-helmet). Set correct HTTP kode status. There’s no peringkat bonus untuk SSR — ini hanya membuat konten reliably dapat diindeks. untuk umum rendering mechanics, see JavaScript SEO dan headless CMS topics.

What actually membuat React hard untuk SEO

React adalah sebuah component-based JavaScript library, dan out dari box — buat React App, Vite + React — ini runs client-side. server mengembalikan sebuah near-empty document (famously hanya sebuah <div id="root"></div>) plus sebuah bundle dari JavaScript, dan browser executes itu JavaScript untuk construct DOM. Contrast itu dengan sebuah server-rendered halaman (WordPress, sebuah Rails app), where full HTML — konten, heading, tautan — arrives di very pertama respons.

So pertanyaan itu decides everything adalah: what’s di raw HTML sebelum apa pun JavaScript runs? untuk sebuah default React app, jawaban adalah “almost nothing.” (terjemahan) “almost nothing.” Right-click → View Source pada sebuah CRA app dan Anda’ll see shell, not konten. itu’s exactly what sebuah crawler gets pada -nya pertama fetch.

untuk menjadi precise tentang where responsibility actually sits: React library isn’t CSR-hanya. React DOM ships client rendering (createRoot), server rendering (streaming dan static APIs), dan hydration APIs — library mendukung semua dari ini. empty-shell masalah adalah sebuah property dari default toolchain (buat React App, Vite + React dengan no server), which wires up hanya client APIs dan nothing itu renders untuk HTML pada server. Swap toolchain — Next.js, Remix, atau React’s own server-rendering APIs — dan yang sama library ships full HTML pada pertama respons.

ini adalah React-spesifik application dari broader JavaScript SEO masalah — go there untuk umum failure modes (parity, interaction, state, timing). Here I’ll focus pada what’s spesifik untuk React dan cara fix ini.

How Google actually processes sebuah React app

Google handles JavaScript di three phases: crawl → render → indeks. Googlebot fetches URL, rendered DOM adalah dibangun later oleh Web rendering Service (WRS) — sebuah evergreen versi dari Chromium, yang sama mesin sebagai Chrome — dan lalu rendered output gets terindeks dan -nya tautan extracted. 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

penting nuance adalah when rendering happens. rendering adalah resource-intensive, so ini adalah queued separately dari initial crawl. Martin Splitt described flow plainly: “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.” (terjemahan) “kami melakukan sebuah HTTP permintaan, dan kami get something back … beberapa barebone HTML dan semua ini melakukan adalah muat JavaScript dan run JavaScript. lalu, ini HTML … goes ke rendering. rendering runs JavaScript — boom!, sebuah lot dari konten happens itu wasn’t there sebelum.” untuk sebuah CSR React halaman, ** “boom” (terjemahan) “boom” adalah Anda entire halaman** — none dari ini exists until itu render langkah runs.

sebuah caveat worth carrying: don’t di atas-indeks pada old “two waves of indexing” (terjemahan) “two waves dari pengindeksan” model. Splitt himself walked ini back, calling wave “an oversimplification.” (terjemahan) “sebuah oversimplification.” practical takeaway isn’t “there’s a formal Wave 2 with defined timing” (terjemahan) “there’s sebuah formal Wave 2 dengan defined timing” — ini adalah itu rendering adalah sebuah distinct, deferrable, fallible langkah, dan CSR puts 100% dari Anda konten pada wrong side dari ini.

Two more facts tentang renderer itu bite React apps specifically:

  • ini adalah stateless. Googlebot melakukan not retain localStorage, sessionStorage, atau cookies antara halaman memuat. apa pun konten atau routing itu depends pada client-side state adalah invisible untuk crawler.
  • ini dapat give up. renderer enforces sebuah timeout. jika Anda main konten memuat slowly — besar bundles, waterfalls dari API panggilan — rendering dapat finish sebelum Anda konten arrives, dan Google indeks incomplete halaman.

render-timeout trap (dan why ini membuat duplicates)

ini failure mode adalah rarely dijelaskan well, dan ini adalah paling damaging one untuk React apps. Gary Illyes described ini directly: “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.” (terjemahan) “I memiliki sebuah bunch dari emails di my inbox where issue adalah itu centerpiece took forever untuk muat, so rendering timed out … dan kami adalah left dengan sebuah bunch dari halaman itu hanya memiliki boilerplate. dengan hanya boilerplate, itu halaman adalah dups.”

Walk melalui what itu berarti untuk sebuah CSR React app. Anda header, nav, dan footer adalah boilerplate itu memuat fast. Anda actual halaman konten — bagian itu membuat setiap URL unique — adalah fetched dan rendered oleh JavaScript, dan ini memuat slowly. rendering times out. Google adalah left dengan header + nav + footer pada setiap URL. Now setiap halaman looks identical, dan Google marks them sebagai duplicates dari setiap lainnya di Search Console.

Illyes’s own fix adalah actionable bit: “Try to restructure the js calls such that the content (including marginal boilerplate) loads first and see if that helps.” (terjemahan) “Try untuk restructure js panggilan such itu konten (including marginal boilerplate) memuat pertama dan see jika itu helps.” tetapi more durable jawaban adalah untuk not depend pada render langkah untuk Anda main konten di semua — which berarti SSR atau SSG.

rendering strategies untuk React

ini adalah single sebagian besar impactful decision. options, roughly worst untuk best untuk SEO:

  • CSR (default React). server mengirim shell; browser membangun everything. konten adalah delayed oleh render queue dan exposed untuk timeout. Worst untuk SEO. Fine untuk authenticated dashboards Anda tidak ingin terindeks anyway.
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. bangun-time rendering without sebuah full SSR framework — alat like react-snap atau sebuah prerender service crawl Anda app dan save static HTML. Lighter-weight; berfungsi untuk simpler, mostly-static situs.
  • SSG (Static situs Generation). HTML dibangun once di deploy time dan disajikan sebagai static files. Fastest, konten selalu present di raw HTML. Limited untuk highly dynamic atau per-pengguna konten; besar situs get slow membangun.
  • SSR (rendering sisi server). server executes React per permintaan dan mengirim full HTML. konten immediately available untuk crawler; selalu fresh. Costs sebuah Node.js server dan slightly higher TTFB.
  • Hybrid / ISR (Incremental Static Regeneration). sebuah Next.js fitur itu regenerates static halaman di background — static speed dengan periodic freshness.
Strategykonten di initial HTML?SEO riskBest untuk
CSR (raw React)NoHighestLogged-di dashboards, not-terindeks apps
Pre-renderingYes (bangun time)rendahkecil, mostly-static situs
SSGYes (bangun time)LowestBlogs, docs, marketing
SSRYes (per permintaan)rendahFresh, dynamic konten
ISR / hybridYesrendahkonten itu perubahan hourly/daily

dan one strategy untuk skip pada baru membangun: dynamic rendering — detecting crawler pengguna-agent dan serving ini sebuah pre-rendered versi while pengguna get CSR. Google now panggilan ini “a workaround and not a long-term solution” (terjemahan) “sebuah workaround dan not sebuah panjang-istilah solusi” itu “creates additional complexities and resource requirements,” (terjemahan) “membuat additional complexities dan resource requirements,” dan recommends rendering sisi server, static rendering, atau hydration instead. (Bing recommended dynamic rendering back di 2018, tetapi itu guidance adalah dated — since 2019 Bingbot renders via Microsoft Edge / Chromium, dan SSR/SSG adalah right panggil there too.)

There’s sebuah myth worth killing here: SSR adalah not sebuah peringkat boost. sebagai John Mueller put ini, “there are no SEO ranking bonuses for implementing it one way or another” (terjemahan) “tidak ada SEO peringkat bonuses untuk implementing ini one cara atau lainnya” — berbeda rendering metode adalah “just different ways of making the content indexable.” (terjemahan) “hanya berbeda cara dari membuat konten dapat diindeks.” SSR’s nilai adalah reliable indexability (dan sering better Core Web Vitals dari sebuah faster pertama Contentful Paint), not sebuah magic peringkat lever.

Hydration harus match exactly — itu’s sebuah bug boundary, not sebuah SEO technique

SSR dan SSG both hand browser HTML itu sudah memiliki Anda konten di ini. React lalu memiliki untuk attach untuk itu markup pada client, dan itu’s sebuah berbeda API dari sebuah plain client render:

  • createRoot renders React ke sebuah DOM node dari scratch — no existing markup expected. gunakan ini untuk CSR-hanya apps.
  • hydrateRoot attaches React untuk HTML itu react-dom/server sudah generated, dan expects client’s pertama render untuk produce output identical untuk what server dikirim. jika Anda’re SSR/SSG, Anda ingin hydrateRoot, not createRoot — calling createRoot pada server-rendered markup berarti React discards ini dan re-renders dari scratch, throwing away exact SEO benefit Anda siapkan SSR/SSG untuk get.
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

Mismatches antara server dan client output adalah sebuah nyata risk di React apps doing SEO fixes — sebuah Date.now() di sebuah judul, sebuah locale-dependent format, sebuah if (typeof window !== 'undefined') branch. React’s own docs adalah blunt tentang what happens lalu: ini warns tentang mismatches di development, tetapi “there are no guarantees that attribute differences will be patched up in case of mismatches.” (terjemahan) “tidak ada guarantees itu attribute differences akan menjadi patched up di case dari mismatches.” guidance adalah untuk treat mismatches sebagai bugs dan fix them — not untuk suppress warning dan assume konten parity. untuk SEO specifically: don’t assume Anda rendered konten dan metadata match what server dikirim hanya because halaman looks right di browser. Diff server HTML terhadap post-hydration DOM directly ( View Source vs. Inspect Element periksa dari testing bagian below adalah fast versi dari ini) alih-alih trusting sebuah clean console.

React Router dan struktur URL

React Router handles navigation di browser without server round-trips, which adalah fine untuk SEO jika configured correctly:

  • gunakan History API, not hash routing. BrowserRouter menggunakan pushState dan produces clean, dapat di-crawl URLs (/products). HashRouter produces /#/products, dan Google cannot reliably resolve hash-based URLs — old AJAX-crawling scheme itu dibuat them berfungsi adalah deprecated. gunakan History API.
  • ** server harus handle itu URLs too.** dengan History API routing, setiap “page” (terjemahan) “halaman” perlu sebuah nyata URL server dapat respond untuk — critical untuk SSR, dan necessary so sebuah direct hit atau refresh pada /products doesn’t 404.
  • <Link> renders sebuah nyata anchor. React Router’s <Link> component outputs sebuah <a href>, which adalah dapat di-crawl. Navigation dibangun pada onClick handlers without sebuah anchor adalah not dapat di-crawl — Google hanya mengikuti nyata <a href> tautan.

Managing metadata: react-helmet, react-helmet-async, dan React 19’s native tags

melalui React 18, React tidak pernah updated document <head> pada route perubahan natively — setiap route’s <title>, deskripsi meta, canonical, dan Open Graph / Twitter tags memiliki untuk menjadi set oleh sebuah library. React 19 changed itu: components dapat render <title>, <meta>, dan <link> tags directly, dan React hoists them untuk <head> pada -nya own — berfungsi dengan client-hanya apps, streaming SSR, dan server Components alike. React 19.2 adalah saat ini stable release sebagai dari mid-2026, so ini now applies untuk apa pun app pada sebuah saat ini React versi.

itu berarti right jawaban depends pada Anda React versi dan what Anda actually perlu:

  • React 19, standalone app, hanya basic tags. Render <title>/<meta>/<link> di Anda components directly — no library needed.
  • React 19, tetapi Anda perlu htmlAttributes/bodyAttributes, SSR context serialization, onChangeClientState, prioritizeSeoTags, atau titleTemplate. Native hoisting doesn’t cover ini — gunakan react-helmet-async. -nya own docs panggil ini out directly: without itu spesifik perlu, Anda dapat not perlu package di semua pada React 19.
  • React 18 atau earlier, standalone app. Native hoisting doesn’t exist yet — gunakan react-helmet-async. ini adalah actively maintained (major versi 3, dan ini detects Anda React versi di runtime) dan mendukung SSR.
  • ** original react-helmet.** Don’t gunakan ini, pada apa pun React versi. ini adalah unmaintained — no release since 2020 — dan memiliki known bugs di bawah React 18’s concurrent rendering.
  • Next.js apps. gunakan Next’s own Metadata API ( metadata export / generateMetadata di App Router) regardless dari React versi — don’t bolt pada Helmet, dan don’t rely pada native React tag hoisting either. framework owns document di sebuah Next.js app.

One reliability aturan carries di atas dari JavaScript SEO umumnya, dan applies no penting which dari above Anda gunakan: HTML-tingkat metadata beats JS-injected metadata. sebuah canonical tag injected oleh client-side JavaScript adalah far less reliable daripada one present di server-rendered HTML — which adalah lainnya argument untuk SSR/SSG.

AI crawler membuat ini urgent

2026 wrinkle: rendering perilaku untuk GPTBot (OpenAI), ClaudeBot (Anthropic), PerplexityBot, dan lainnya agents adalah provider- dan versi-spesifik. sebuah CSR React app ships setiap crawler yang sama empty shell pertama Googlebot fetch sees, dan saat ini provider documentation melakukan not establish one shared render langkah itu akan fill ini di. sebagai generative mesin become sebuah bigger penemuan surface, SSR/SSG stops menjadi sebuah Google-hanya concern: raw HTML maximizes coverage without assuming sebuah universal crawler limitation. ( CMS headless topic covers ini AI-crawler reality di more depth.)

Testing what Google actually sees

Don’t trust Anda browser — Anda DevTools Inspector menampilkan rendered DOM (post-JavaScript), which adalah exactly what sebuah crawler-without-JS melakukan not see. gunakan right alat:

  • View Source vs. Inspect Element. View Source adalah raw HTML (what crawler get sebelum JS). Inspect Element adalah rendered DOM. jika konten adalah di Inspect tetapi missing dari View Source, ini adalah JavaScript-dependent.
  • pemeriksaan URL alat (Search Console) — paling authoritative periksa. Run sebuah live test dan lihat rendered HTML, screenshot, dan halaman resources / console messages untuk see what Google actually rendered dan what failed untuk muat.
  • Rich hasil Test — sebuah quick rendered-HTML periksa without verifying situs.
  • Disable JavaScript di DevTools dan reload — sebuah fast simulation dari sebuah crawler itu doesn’t execute JS (dan sebuah decent proxy untuk what AI crawler see).
  • Search Console Coverage report — “Discovered, currently not indexed” (terjemahan) “ditemukan, currently not terindeks” dapat signal sebuah render queue backlog; clusters dari duplicate halaman dapat signal render-timeout boilerplate trap.
  • JS-rendering crawler — Ahrefs situs Audit dan Screaming Frog (JS-rendering mode) render halaman di scale so Anda dapat diff raw vs. rendered di seluruh whole situs.

Next.js dan Remix ( practical jawaban)

jika SEO penting dan Anda’re pada raw CSR React, migrating untuk sebuah framework itu renders pada server adalah biasanya right move. Next.js adalah purpose-dibangun untuk ini — SSR dan SSG out dari box, ISR, App Router, sebuah dibangun-di Metadata API, automatic code splitting, dan image optimization. Remix adalah web-standards alternative, dibangun pada fetch/permintaan/respons dengan SSR oleh default dan sebuah strong progressive-enhancement story. Next.js gets -nya own deep dive — I’m keeping ini brief here pada purpose. poin untuk React SEO adalah narrower: framework exists untuk move Anda konten out dari browser-hanya render langkah dan ke initial HTML.

React adalah baik untuk SEO when Anda treat rendering sebagai sebuah architecture decision alih-alih sebuah afterthought. Pick SSR atau SSG untuk anything itu perlu untuk peringkat, pertahankan tautan dan routing honest, manage metadata per route, dan let Google’s own alat — not Anda browser — tell Anda what actually rendered.

Add an expert note

Pin an expert quote

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