JavaScript SEO: How Google Handles JS Sites and What to Fix
Google can run JavaScript. That has been true for years, and Googlebot uses an up-to-date version of Chromium to render pages. So the old fear that "Google can't see React sites" is out of date. The more accurate statement is that JavaScript adds steps, and every extra step is a place where things can go wrong.
If you build with React, Vue, Angular or a similar framework, or you run a store or SaaS app where content loads through API calls, this is the part of technical SEO you can't ignore.
How Google processes a JavaScript page
Google describes three phases:
- Crawling. Googlebot fetches the URL and gets the initial HTML. It checks robots.txt first.
- Rendering. The page goes into a queue for rendering, where a headless browser runs the JavaScript and builds the final page. This can happen quickly, but it's a separate step and it uses more resources than reading plain HTML.
- Indexing. Google indexes the rendered result and extracts links to crawl next.
If the important content, links and meta tags are already in the initial HTML, Google doesn't need to wait for rendering to understand the page. If they only appear after JavaScript runs, you depend on rendering working correctly, every time.
Other search engines and many social media preview bots are less capable at rendering than Google. If you care about Bing, link previews on WhatsApp and LinkedIn, or AI crawlers, server-rendered HTML is safer still.
Rendering approaches
| Approach | How it works | SEO notes |
|---|---|---|
| Client-side rendering (CSR) | Server sends a near-empty HTML shell; the browser builds everything | Most risk; content depends on rendering |
| Server-side rendering (SSR) | Server builds full HTML per request, then JS takes over | Content is in the initial HTML; good default for dynamic pages |
| Static site generation (SSG) | HTML built at deploy time | Fast and crawlable; great for blogs and marketing pages |
| Hybrid / incremental | Mix of the above per route | What frameworks like Next.js and Nuxt encourage |
Google now describes "dynamic rendering", serving pre-rendered HTML only to bots, as a workaround rather than a recommended long-term solution. If you're starting fresh, use SSR or static generation for any page you want to rank.
Common JavaScript SEO problems
Links that aren't links
Googlebot follows <a href="/page"> elements. It does not click buttons or run onclick handlers to discover URLs. A product grid where each item is a <div> with a click handler, or a "Load more" button that fetches results without real URLs, hides those pages from discovery.
Use proper anchor tags with real href values, even if your router intercepts the click. Make pagination available through crawlable URLs.
Content that needs interaction
Googlebot doesn't scroll like a user, click tabs or type into search boxes. Content loaded only after a click or on infinite scroll may never be rendered. If it matters for SEO, have it present in the DOM on load (collapsed is fine) or reachable at its own URL.
Meta tags set by JavaScript
Titles, meta descriptions, canonicals and robots tags changed by JavaScript after load can be picked up, but conflicts cause trouble. A classic bug: the initial HTML contains noindex and JavaScript is supposed to remove it. Google may see the noindex in the initial HTML and skip rendering the page at all. Put the correct tags in the server response. Tools like the free meta tag generator help you check what each page should carry.
Blocked resources
If robots.txt blocks your JS bundles or the API endpoints the page calls, Google can't render the content. Check the robots.txt guide and make sure scripts and needed APIs are crawlable.
Hash-based URLs
URLs like example.com/#/products/123 are treated as one page because Google generally ignores the fragment. Use the History API so each view has a real path.
Soft 404s in single-page apps
An SPA often returns a 200 status for every route, then shows "Not found" with JavaScript. Google may index those empty pages or flag them as soft 404s. Return proper status codes from the server, or for client-only setups, add a noindex or redirect to a URL that returns a real 404.
Slow or failing API calls
If the content comes from an API that's slow, rate-limits Googlebot or times out, the rendered page may be empty. Google's renderer won't wait indefinitely.
How to test
- URL Inspection in Search Console. Use "Test live URL" and view the rendered HTML and screenshot. If your product description, prices or links are missing there, Google isn't seeing them.
- Rich Results Test. Also shows rendered HTML and is available without Search Console access.
- View source vs Inspect element. View source shows the initial HTML; the browser's Elements panel shows the rendered DOM. If the important content only appears in the second, you rely on rendering.
- Crawl with JavaScript rendering on and off. Compare word counts, links and titles between the two crawls.
Practical advice by situation
Marketing site or blog on React: use a framework with static generation or SSR (Next.js, Astro, Remix, Nuxt). There's little reason to client-render pages whose content rarely changes.
Ecommerce on a headless setup: make sure product, category and listing pages are server-rendered with real links. Client-side rendering is fine for the cart and account areas, which you'll noindex anyway.
SaaS app: the logged-in app doesn't need to be indexable. Keep public pages, docs and the blog server-rendered and separate from the app shell.
Site architecture and internal linking strategy matter just as much on JavaScript sites; the difference is making sure those links exist in a form crawlers can follow.
Pick your most important template, run it through URL Inspection's live test, and compare the rendered HTML to what a visitor sees. Anything missing from Google's version is your first job.