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.
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 adalah not buruk untuk SEO — tetapi cara sebagian besar React apps adalah dibangun adalah. oleh default, React membangun halaman di pengunjung’s browser, so when sebuah mesin pencari pertama fetches Anda URL ini gets sebuah nearly empty halaman. Google dapat biasanya fill di blanks oleh running Anda JavaScript, tetapi ini adalah slower dan riskier daripada hanya handing ini finished HTML. fix adalah untuk render Anda halaman pada sebuah server atau di bangun time — biasanya dengan sebuah framework like Next.js.
Why React adalah berbeda
sebagian besar situs web — sebuah WordPress blog, say — kirim mesin pencari sebuah complete halaman:
server membangun HTML dan ships ini, headline dan semua. sebuah standard React app melakukan
opposite. server mengirim sebuah almost-empty shell (basically sebuah empty <div>),
dan lalu JavaScript runs di browser untuk bangun nyata halaman.
itu’s great untuk slick, app-like experiences. ini adalah sebuah masalah untuk SEO, because pertama thing sebuah crawler downloads adalah itu empty shell. Anda konten isn’t there yet — ini hanya menampilkan up setelah JavaScript runs.
dapat’t Google hanya run JavaScript?
Yes — Google runs sebuah nyata, up-untuk-date versi dari Chrome behind scenes dan dapat execute Anda JavaScript untuk see finished halaman. So React konten dapat get terindeks. 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
tetapi ada catches:
- ini adalah delayed. Google melakukan rendering later, di sebuah separate langkah itu’s queued up. So Anda konten dapat take longer untuk tampilkan up di search.
- ini dapat fail. jika Anda halaman adalah slow untuk muat -nya konten, Google’s renderer dapat give up sebelum konten appears — dan indeks sebuah blank-ish halaman.
- lainnya crawler vary. Bing handles JavaScript less reliably, dan AI providers melakukan not publish one shared rendering contract. apa pun crawler itu fetches hanya initial HTML akan see sebuah default React app sebagai empty.
sederhana fix
Get Anda konten ke HTML sebelum ini reaches browser. Two cara:
- rendering sisi server (SSR) — sebuah server membangun full halaman untuk setiap permintaan.
- Static situs generation (SSG) — halaman adalah dibangun ke finished HTML ahead dari time.
easiest path untuk either adalah Next.js, sebuah framework dibangun pada React itu melakukan ini untuk Anda. (Remix adalah lainnya baik option.) dengan SSR atau SSG, Anda React situs hands crawler sebuah complete halaman — dan ini adalah sebagai search-friendly sebagai apa pun normal situs web.
sebuah few lainnya things untuk get right
- gunakan normal-looking URLs (
/products), not hash URLs (/#/products) — Google dapat’t reliably indeks hash ones. - membuat Anda tautan nyata tautan (
<a href>), not clickable<div>s. - Give setiap halaman -nya own judul dan deskripsi itu update when halaman perubahan.
ingin deeper versi — how Google’s renderer actually berfungsi, render-timeout trap itu membuat duplicate halaman, rendering-strategy comparison, dan cara test what Google sees? Switch untuk Advanced tab.
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(notcreateRoot) 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.
- Pre-rendering. bangun-time rendering without sebuah full SSR framework — alat like
react-snapatau 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.
| Strategy | konten di initial HTML? | SEO risk | Best untuk |
|---|---|---|---|
| CSR (raw React) | No | Highest | Logged-di dashboards, not-terindeks apps |
| Pre-rendering | Yes (bangun time) | rendah | kecil, mostly-static situs |
| SSG | Yes (bangun time) | Lowest | Blogs, docs, marketing |
| SSR | Yes (per permintaan) | rendah | Fresh, dynamic konten |
| ISR / hybrid | Yes | rendah | konten 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:
createRootrenders React ke sebuah DOM node dari scratch — no existing markup expected. gunakan ini untuk CSR-hanya apps.hydrateRootattaches React untuk HTML itureact-dom/serversudah generated, dan expects client’s pertama render untuk produce output identical untuk what server dikirim. jika Anda’re SSR/SSG, Anda inginhydrateRoot, notcreateRoot— callingcreateRootpada server-rendered markup berarti React discards ini dan re-renders dari scratch, throwing away exact SEO benefit Anda siapkan SSR/SSG untuk get.
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.
BrowserRoutermenggunakanpushStatedan produces clean, dapat di-crawl URLs (/products).HashRouterproduces/#/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
/productsdoesn’t 404. <Link>renders sebuah nyata anchor. React Router’s<Link>component outputs sebuah<a href>, which adalah dapat di-crawl. Navigation dibangun padaonClickhandlers 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, SSRcontextserialization,onChangeClientState,prioritizeSeoTags, atautitleTemplate. Native hoisting doesn’t cover ini — gunakanreact-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 (
metadataexport /generateMetadatadi 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.
AI summary
sebuah condensed take pada Advanced versi:
- React isn’t buruk untuk SEO — CSR oleh default adalah. React library mendukung client,
server, static, dan streaming rendering; ini adalah default CRA/Vite toolchain (no
server) itu ships sebuah empty
<div id="root">shell dan membangun DOM di browser, so raw HTML sebuah crawler fetches memiliki tanpa konten. - Google dapat render React via Web rendering Service (evergreen Chromium), tetapi rendering adalah queued separately, dapat menjadi delayed, dan adalah stateless (no cookies/localStorage/sessionStorage antara memuat).
- ** render-timeout trap:** jika main konten memuat slowly, rendering times out dan Google indeks sebuah boilerplate-hanya halaman. di seluruh banyak URLs itu look identical dan get flagged sebagai duplicates (Gary Illyes’s documented failure mode). Fix: muat konten pertama — atau better, don’t depend pada render langkah (SSR/SSG).
- rendering strategies, best→worst untuk SEO: SSG (lowest risk) ≈ SSR ≈ pre-render > ISR/hybrid > CSR (highest risk). Dynamic rendering adalah deprecated — Google recommends SSR, static rendering, atau hydration.
- No peringkat bonus untuk SSR — Mueller: “no SEO ranking bonuses for implementing it one way or another.” (terjemahan) “no SEO peringkat bonuses untuk implementing ini one cara atau lainnya.” ini hanya membuat konten reliably dapat diindeks.
- Hydration adalah sebuah bug boundary, not sebuah technique: SSR/SSG apps hydrate dengan
hydrateRoot(notcreateRoot), which expects client’s pertama render untuk match server’s exactly. React warns pada mismatches di dev tetapi doesn’t guarantee patching them — treat mismatches sebagai bugs dan verify server vs. post-hydration DOM directly. - React Router: gunakan History API (
BrowserRouter), not hash routing; server harus handle itu URLs;<Link>renders dapat di-crawl<a href>—onClick-hanya nav doesn’t. - Metadata: React 19 natively hoists
<title>/<meta>/<link>untuk<head>untuk standalone apps needing hanya basics. pada React 18, atau untuk advanced perlu (SSR context,titleTemplate), gunakan react-helmet-async — tidak pernah original react-helmet, unmaintained since 2020. di Next.js, gunakan Metadata API regardless dari React versi. HTML-tingkat beats JS-injected. - AI-crawler rendering adalah provider-spesifik — CSR React depends pada client execution itu setiap crawler dapat atau dapat not mendukung. SSR/SSG puts konten di initial HTML dan maximizes coverage.
- Test dengan View Source vs. Inspect, pemeriksaan URL (rendered HTML + screenshot + console), Rich hasil Test, JS-disabled reload, dan sebuah JS-rendering crawler.
- Next.js / Remix adalah practical fix — mereka move konten ke initial HTML.
Official documentation
Primary-source documentation dari mesin pencari.
- memahami JavaScript SEO Basics — crawl → render → indeks pipeline, SPAs, History API, canonical URLs dengan JS, dan meaningful HTTP kode status.
- Fix Search-Related JavaScript masalah — soft-404 handling di SPAs, stateless renderer (no cookies/localStorage), dan testing dengan pemeriksaan URL.
- Dynamic rendering (deprecated workaround) — why Google deprecated ini dan what untuk gunakan instead (SSR, static rendering, hydration).
- Introducing sebuah baru JavaScript SEO video series — Martin Splitt’s series, covering React, Angular, dan Vue specifically.
- di-Depth Guide untuk How Google Search berfungsi — where rendering sits di crawl → indeks → sajikan.
Bing / Microsoft
- baru evergreen Bingbot (Microsoft Edge) — Bingbot rendering JavaScript via yang sama Chromium platform sebagai Googlebot.
- bingbot Series: JavaScript, Dynamic rendering, dan Cloaking — Bing’s older (2018) take; berguna untuk historical dynamic-rendering recommendation.
Technical reference
- React v19 (release notes) — native mendukung untuk rendering
<title>,<meta>, dan<link>tags di components, hoisted automatically untuk<head>. - createRoot / hydrateRoot (React docs) — client-render vs. hydration API split, dan caveat itu mismatches aren’t guaranteed untuk menjadi patched.
- react-helmet-async (npm) — maintained fork untuk managing
<head>di standalone React apps, React 18 atau advanced React 19 perlu.
Quotes dari source
pada—record statements dari Google’s Search team. setiap search-mesin deep tautan jumps untuk quoted passage pada source halaman; staff quotes below adalah ditautkan untuk coverage itu reproduced them.
Google docs — SPAs dan dynamic rendering
- “Single-page applications (SPA) are websites that load an HTML document once and fetch any additional content using JavaScript APIs.” (terjemahan) “Single-halaman applications (SPA) adalah situs web itu muat sebuah HTML document once dan fetch apa pun additional konten menggunakan JavaScript APIs.” — Google Search Central docs. Jump untuk quote
- “Dynamic rendering was a workaround and not a long-term solution for problems with JavaScript-generated content in search engines.” (terjemahan) “Dynamic rendering adalah sebuah workaround dan not sebuah panjang-istilah solusi untuk masalah dengan JavaScript-generated konten di mesin pencari.” — Google Search Central docs. Jump untuk quote
Martin Splitt, Google — how JavaScript halaman adalah terindeks
- “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.” (terjemahan) “What kami melakukan adalah kami melakukan sebuah HTTP permintaan, dan kami get something back, right — beberapa HTML, maybe ini adalah sebuah barebone HTML dan semua ini melakukan adalah muat JavaScript dan run JavaScript. lalu, ini HTML itu kami got dari original HTTP GET permintaan dari crawl, goes ke rendering. rendering runs JavaScript — boom!, sebuah lot dari konten happens itu wasn’t there sebelum.” Read coverage (mesin pencari Journal)
- pada two-waves model menjadi imprecise: “there’s no such thing as the second wave of crawling-ish. The wave is an oversimplification.” (terjemahan) “there’s no such thing sebagai kedua wave dari crawling-ish. wave adalah sebuah oversimplification.” Read coverage (mesin pencari Roundtable)
Gary Illyes, Google — render-timeout duplicate-konten trap
- “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.” (terjemahan) “I memiliki sebuah bunch dari emails di my inbox where issue adalah itu centerpiece took forever untuk muat, so rendering timed out (my sebagian besar mungkin explanation) dan kami adalah left dengan sebuah bunch dari halaman itu hanya memiliki boilerplate. dengan hanya boilerplate, itu halaman adalah dups.”
- “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.” (terjemahan) “melakukan Anda memiliki sebuah JavaScript-heavy situs dan Anda see lots dari dups reported di Search Console? Try untuk restructure js panggilan such itu konten (including marginal boilerplate) memuat pertama dan see jika itu helps.” Read post (LinkedIn)
John Mueller, Google — no peringkat bonus untuk rendering choice
- “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.” mereka’re “just different ways of making the content indexable (as is client side rendering).” (terjemahan) “hanya berbeda cara dari membuat konten dapat diindeks (sebagai adalah client side rendering).” Read coverage (mesin pencari Roundtable)
React SEO checklist
sebuah pass untuk confirm crawler dapat see dan indeks Anda React app:
- penting konten appears di View Source (raw HTML), not hanya di rendered DOM — jika ini adalah missing, Anda’re depending pada CSR.
- halaman itu perlu untuk peringkat gunakan SSR atau SSG (Next.js, Remix, atau sebuah pre-render langkah), not raw rendering sisi klien.
- Main konten memuat fast dan pertama — no slow API waterfalls itu dapat trip render timeout ke sebuah boilerplate-hanya halaman.
- Routing menggunakan History API (
BrowserRouter), notHashRouter/#/URLs. - server dapat respond untuk setiap client-side route (no 404 pada direct hit atau refresh).
- Navigation menggunakan nyata
<a href>tautan (React Router<Link>), notonClick-hanya handlers pada<div>/<button>. - setiap route sets sebuah unique
<title>, deskripsi meta, canonical, dan OG tags itu update pada navigation. - Metadata matches Anda versi: React 19 native
<title>/<meta>/<link>tags untuk basics, react-helmet-async untuk React 18 atau advanced perlu (tidak pernah deprecatedreact-helmet), atau Next.js Metadata API jika pada Next.js. - jika server-rendered, Anda’re hydrating dengan
hydrateRoot(notcreateRoot), dan apa pun dev-mode hydration mismatch warning adalah treated sebagai sebuah bug untuk fix. - Client-side “not found” (terjemahan) “tidak ditemukan” routes mengembalikan nyata 404 (atau sebuah
noindex), not sebuah soft-404 dengan sebuah200status. - JavaScript dan CSS adalah not blocked di
robots.txt(Google won’t render dari blocked files). - No critical konten depends pada cookies / localStorage / sessionStorage ( renderer adalah stateless).
- Verified di pemeriksaan URL: rendered HTML dan screenshot tampilkan Anda nyata konten.
- Coverage report diperiksa untuk “Discovered, currently not indexed” (terjemahan) “ditemukan, currently not terindeks” (render backlog) dan duplicate clusters (render-timeout trap).
mental models
1. satu-satunya pertanyaan itu penting: what’s di raw HTML? View Source sebelum apa pun JavaScript runs adalah what sebuah pertama-fetch crawler — dan sebagian besar AI crawler, forever — see. jika Anda konten isn’t there, Anda memiliki sebuah React SEO masalah regardless dari how baik halaman looks di Anda browser.
2. rendering adalah sebuah separate, fallible langkah. crawl → render → indeks. CSR puts 100% dari Anda konten pada far side dari render langkah, which adalah queued, delayed, stateless, dan dapat time out. SSR/SSG move Anda konten sebelum itu langkah. Don’t di atas-trust “two waves” (terjemahan) “two waves” model — Splitt himself called ini sebuah oversimplification.
3. boilerplate-duplicate failure mode. Slow konten + render timeout = setiap URL renders sebagai header/nav/footer hanya = Google sees duplicates. fix adalah structural: muat konten pertama, atau stop depending pada render langkah.
4. rendering decision tree.
- Public konten itu harus peringkat atau menjadi cited oleh AI → SSG (static) atau SSR (fresh).
- Mostly static (blog, docs, marketing) → SSG, atau ISR pada sebuah timer.
- Frequently changing, harus menjadi fresh → SSR.
- Logged-di dashboard, not dimaksudkan untuk menjadi terindeks → CSR adalah fine.
- baru bangun itu perlu SEO → reach untuk Next.js / Remix, not dynamic rendering.
5. HTML-pertama, JS-kedua untuk setiap SEO signal. konten, tautan, canonicals, judul, data terstruktur — get them ke server-rendered HTML. Treat JS-injected SEO signals (including JS tag canonical dan react-helmet pada CSR) sebagai sebuah fallback, not plan: Google sees them late, dan AI crawler not di semua.
React SEO — cheat sheet
rendering modes di sebuah glance
| Mode | konten di initial HTML? | SEO risk | gunakan untuk |
|---|---|---|---|
| CSR (raw React) | No | Highest | Logged-di dashboards, not-terindeks apps |
| Pre-rendering (react-snap) | Yes (bangun time) | rendah | kecil, mostly-static situs |
| SSG | Yes (bangun time) | Lowest | Blogs, docs, marketing |
| SSR | Yes (per permintaan) | rendah | Fresh, dynamic konten |
| ISR / hybrid (Next.js) | Yes | rendah | Hourly/daily konten |
| Dynamic rendering | bot-hanya | Deprecated | Don’t — gunakan SSR/SSG/hydration |
umum React SEO mistakes → fixes
| Mistake | Fix |
|---|---|
| konten hanya di rendered DOM (CSR) | SSR / SSG / pre-render |
Hash URLs (/#/path) | History API (BrowserRouter) |
onClick navigation, no anchor | nyata <a href> / React Router <Link> |
| Meta tags don’t update pada route | React 19: native <title>/<meta>/<link>. Older/advanced: react-helmet-async. Next.js: Metadata API |
react-helmet (original, apa pun React versi) | Switch untuk react-helmet-async atau React 19 native tags |
createRoot digunakan pada server-rendered HTML | gunakan hydrateRoot instead — createRoot discards server markup |
| Hydration mismatch warning suppressed | Treat ini sebagai sebuah bug dan fix server/client difference |
| Slow konten → render timeout → dups | muat main konten pertama; move untuk SSR/SSG |
Client-side 404 mengembalikan 200 | nyata 404 status atau noindex |
Blocked .js / .css di robots.txt | Allow them — Google won’t render blocked files |
| konten gated pada cookies/localStorage | Don’t — renderer adalah stateless |
Fast aturan
- renderer adalah evergreen Chromium, queued, stateless, dan times out.
- No peringkat bonus untuk SSR — ini adalah tentang reliable indexability (Mueller).
- AI-crawler rendering varies oleh provider → raw HTML adalah safest coverage baseline.
- di Next.js, gunakan Metadata API — not React Helmet.
- Bing renders JS (via Edge) tetapi less reliably; SSR/SSG adalah safe panggil there too.
periksa what server mengirim sebelum React runs
Put representative dapat diindeks routes di urls.txt. ini menemukan CSR shells dan missing
server-rendered head markup di raw respons:
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.txtkecil byte size adalah hanya sebuah review signal, not sebuah error oleh itself. Compare flagged raw respons dengan rendered HTML dan confirm itu primary text dan dapat di-crawl tautan adalah present.
alat untuk auditing React SEO
- View Source vs. Inspect Element — fastest pertama periksa. View Source adalah raw HTML (what sebuah crawler gets sebelum JS); Inspect Element adalah rendered DOM. konten di Inspect tetapi not View Source adalah JavaScript-dependent.
- pemeriksaan URL (Google Search Console) — sumber kebenaran. Run sebuah live test, lalu view rendered HTML, screenshot, halaman resources (what dimuat vs. adalah blocked), dan console messages untuk see exactly what Google rendered.
- Rich hasil Test — sebuah quick rendered-HTML dan structured-data periksa untuk sebuah single URL without verifying situs.
- Chrome DevTools — disable JavaScript (Command Menu → “Disable JavaScript” (terjemahan) “Disable JavaScript”) dan reload untuk inspect what sebuah HTML-hanya fetcher menerima. ini adalah sebuah coverage periksa, not proof dari apa pun named AI crawler’s saat ini rendering perilaku.
- Search Console Coverage report — watch “Discovered, currently not indexed” (terjemahan) “ditemukan, currently not terindeks” (render backlog) dan duplicate clusters ( render-timeout boilerplate trap).
- JS-rendering crawler — Ahrefs situs Audit dan Screaming Frog SEO Spider (JS-rendering mode) execute JavaScript so Anda dapat diff raw vs. rendered HTML di seluruh situs.
Mistakes React teams actually membuat
Concrete patterns I pertahankan seeing di CSR React apps itu get shipped, not hypothetical ones. setiap adalah sebuah prevention move — catch ini sebelum ini costs Anda pengindeksan.
Shipping raw CRA/Vite CSR untuk halaman itu perlu untuk peringkat
Teams ship buat React App atau Vite + React straight untuk production untuk marketing halaman,
blog posts, atau product halaman — exact konten itu perlu untuk tampilkan up di search.
Why ini adalah wrong: server mengirim sebuah almost-empty <div id="root"> shell; Anda nyata
konten hanya exists setelah JavaScript runs, so ini adalah delayed oleh Google’s render queue dan
dapat menjadi invisible untuk apa pun AI crawler itu fetches initial HTML without client execution.
What untuk melakukan instead: move
anything itu perlu untuk peringkat atau menjadi cited untuk SSR atau SSG — Next.js atau Remix adalah easiest
paths — dan reserve raw CSR untuk logged-di, non-terindeks surfaces like dashboards.
Routing pada hash URLs (HashRouter)
Reaching untuk React Router’s HashRouter because ini adalah path dari least resistance —
no server config needed, berfungsi pada apa pun static host. Why ini adalah wrong: Google dapat’t
reliably resolve /#/products-style URLs; old AJAX-crawling scheme itu dibuat hash
fragments dapat di-crawl adalah deprecated. What untuk melakukan instead: gunakan BrowserRouter (
History API) dan pastikan server responds untuk setiap route ini produces, including sebuah
direct hit atau refresh pada sebuah deep tautan.
membangun navigation pada onClick alih-alih nyata anchors
Wiring up navigation dengan onClick handlers pada sebuah <div> atau <button>, sering because ini
adalah easier untuk style atau hindari default tautan perilaku. Why ini adalah wrong: Google hanya
mengikuti nyata <a href> tautan — sebuah <div> dengan sebuah click handler adalah invisible untuk crawling,
no penting how ini behaves untuk sebuah mouse. What untuk melakukan instead: gunakan React Router’s
<Link> component, which renders sebuah actual <a href> di bawah hood, atau sebuah plain anchor
tag untuk external navigation.
memuat main konten behind sebuah slow API waterfall
Fetching header dan nav fast, lalu chaining several API panggilan sebelum actual halaman konten — bagian itu membuat setiap URL unique — appears. Why ini adalah wrong: Google’s Web rendering Service enforces sebuah timeout; jika Anda centerpiece konten memuat slowly, rendering finishes sebelum ini arrives, dan Google adalah left pengindeksan boilerplate-hanya halaman itu lalu get flagged sebagai duplicates dari setiap lainnya — exact failure mode Gary Illyes described. What untuk melakukan instead: restructure permintaan so main konten memuat pertama, atau hapus dependency pada rendering sisi klien entirely dengan SSR/SSG.
masih menggunakan original react-helmet
Reaching untuk react-helmet untuk per-route <title> dan meta tags because ini adalah
library setiap older tutorial recommends. Why ini adalah wrong: original package adalah
unmaintained — no release since 2020 — dan memiliki known issues di bawah React 18’s concurrent
rendering dan SSR. What untuk melakukan instead: pada React 19, render <title>/<meta>/<link>
directly di Anda components dan let React hoist them (no library needed untuk basics).
pada React 18, atau untuk advanced perlu like SSR context serialization atau titleTemplate, gunakan
react-helmet-async, maintained fork. pada Next.js, gunakan -nya dibangun-di Metadata API dan
don’t bolt Helmet pada top dari ini.
Blocking JavaScript atau CSS di robots.txt
Blocking /static/js/ atau sebuah bundler’s asset folder di robots.txt, sometimes left di atas
dari sebuah old crawl-budget concern atau copied dari lainnya situs’s config. Why ini adalah
wrong: Google dapat’t render what ini isn’t allowed untuk fetch — sebuah blocked bundle berarti
Web rendering Service membangun sebuah incomplete (atau empty) DOM, bahkan though Anda kode sumber
adalah fine. What untuk melakukan instead: allow crawler untuk fetch Anda JS dan CSS, dan confirm ini
dengan pemeriksaan URL alat’s halaman-resources periksa untuk see nothing critical adalah blocked.
Test yourself: React SEO
Five quick pertanyaan pada membuat React apps dapat di-crawl dan dapat diindeks. Pick sebuah jawaban untuk setiap, lalu periksa.
Resources worth Anda time
My related writing
- React SEO: Best Practices untuk membuat ini SEO-Friendly (Ahrefs) — React SEO guide pada Ahrefs, which I reviewed; ini artikel adalah deeper, source-ditautkan treatment.
- JavaScript SEO: sebuah Definitive Guide (Ahrefs) — my full guide untuk underlying rendering mechanics: DOM parity, paling-restrictive-directive aturan, canonical dan meta-tag handling, dan rendering choices. Read ini untuk umum case behind React-spesifik fixes.
- Beginner’s Guide untuk SEO teknis (Ahrefs) — where React/JavaScript SEO fits di bigger picture.
My speaking
- How Search berfungsi (SlideShare) — my walkthrough dari crawling, rendering, pengindeksan, dan peringkat. (My standing disclaimer applies: “This is my understanding of systems… not going to be 100% complete or accurate.” (terjemahan) “ini adalah my understanding dari sistem… not going untuk menjadi 100% complete atau accurate.”)
dari sekitar industry
- memahami JavaScript SEO Basics (Google) — primary-source docs pada SPAs, History API, dan canonical/status-code handling.
- Dynamic rendering (deprecated) (Google) — why dynamic rendering adalah sebuah workaround, not sebuah panjang-istilah solusi.
- Martin Splitt menjelaskan How JavaScript situs adalah terindeks (mesin pencari Journal) — “barebone HTML … boom” (terjemahan) “barebone HTML … boom” explanation dari crawl → render → indeks flow.
- Gary Illyes pada JS-heavy situs dan duplicate konten (LinkedIn) — render-timeout-untuk-duplicates failure mode, di his own kata.
- cara fix SEO teknis issues pada client-side React apps (mesin pencari Land) — sebuah dunia nyata case study pada auditing dan fixing sebuah CSR React app.
- SSR vs. dynamic rendering — no peringkat difference (mesin pencari Roundtable) — Mueller’s “no SEO ranking bonuses” (terjemahan) “no SEO peringkat bonuses” statement.
- react-helmet-async (npm) — maintained head-management library untuk standalone React.
- baru evergreen Bingbot (Microsoft Edge) (Bing) — Bingbot rendering JS via Chromium, like Googlebot.
Videos
- Google Search Central — JavaScript SEO series (YouTube) — Martin Splitt’s official video series covers SEO untuk React, Angular, dan Vue specifically, walking melalui crawl → render → indeks process dan umum fixes. Series announcement · Channel
Log perubahan
Diperbarui 18 Jul 2026.
Ringkasan editorial dan detail perubahan yang tercatat.Detail perubahan
-
Catatan perubahan terperinci saat ini tersedia dalam bahasa Inggris.
-
Catatan perubahan terperinci saat ini tersedia dalam bahasa Inggris.
-
Catatan perubahan terperinci saat ini tersedia dalam bahasa Inggris.
Perbandingan lengkap tidak tersedia — tidak ada cuplikan sebelumnya yang diarsipkan untuk revisi ini.