Technical SEO

JavaScript SEO: what Googlebot actually sees

How Google crawls, queues and renders JavaScript pages, what commonly breaks, which rendering strategy to choose, and how to test what Googlebot really sees.

Google can render JavaScript, but rendering is a separate step that comes after crawling, and a lot can go wrong between the HTML your server sends and the page a browser shows. Many other crawlers, including many AI crawlers, don’t run JavaScript at all. The sites that do well put their content, links and key signals in the initial HTML, and they test what crawlers actually receive.

How Google processes JavaScript

Googlebot fetches a URL, if robots.txt allows it, and reads the HTML response. Pages that return a 200 status join a queue for rendering, where an evergreen (always current) version of Chromium runs the JavaScript. Pages with other status codes may not be rendered at all. Only then does Google index the rendered content and pick up any links that appeared during rendering.

How Google processes a JavaScript page

Links and content in the initial HTML are available at the crawl stage. Everything else waits for rendering.

Two consequences follow. Rendering can lag behind crawling, so content that exists only after JavaScript runs may be picked up later than content in the HTML. And if Google finds noindex in the initial HTML, it may skip rendering altogether, so a script that removes the tag later won’t help.

Once a page is rendered, Google can read the links, text and structured data that JavaScript added. But “can” isn’t the same as “will, reliably and quickly”, and most JavaScript SEO problems live in that gap.

What breaks most often

  • Content that appears only after an interaction. Google doesn’t click, type or scroll. Tabs, accordions and “load more” buttons that fetch content on click keep that content from Google. Put it in the HTML and show or hide it with CSS.
  • Links without an href, and hash routing. Google reliably follows <a> elements with an href. Buttons with click handlers aren’t links, and routes like /#/pricing are fragments, not separate URLs. Use the History API with real paths.
  • noindex or canonicals changed by JavaScript. Google may never render the version in which a script removes noindex. If JavaScript sets a canonical, it should match the one in the HTML, and the page should only have one.
  • Soft 404s in single-page apps. Many apps return 200 for routes that don’t exist and show a “not found” view. Have the server return a real 404. If you can’t, redirect to a URL that does, or add noindex to the error view.
  • Blocked JavaScript or CSS. If robots.txt disallows the files a page needs, Google renders the page without them.
  • Lazy loading triggered by scroll events. Content that waits for a scroll may never load for Google. Use native lazy loading or IntersectionObserver, which load content as it enters the viewport.

Choosing a rendering strategy

Your rendering strategy decides what’s in the initial HTML, and therefore what every crawler can see without running JavaScript.

Rendering strategies for public pages

StrategyWhat crawlers receiveVerdict
Static generation (SSG)Complete HTML, built in advanceBest default
Server-side rendering (SSR)Complete HTML, built for each requestRecommended
Hybrid: server HTML plus hydrationComplete HTML, with JavaScript only where it’s neededRecommended
Client-side rendering (CSR)A near-empty shell until JavaScript runsAvoid for SEO
Dynamic renderingPre-rendered HTML for bots, the full app for usersWorkaround
Anything that sends complete HTML is a safe choice. The risk comes from making crawlers wait for JavaScript.

Static generation suits pages that change rarely, such as service pages and articles. Server-side rendering suits pages that change often or depend on the request, such as stock levels. Most modern frameworks support both and let you mix them route by route. Client-side rendering is fine behind a login, but risky for anything you want found. If a full rebuild isn’t possible yet, start by moving the templates that earn search traffic, such as category and service pages, to server-rendered or static HTML. Google calls dynamic rendering a workaround rather than a long-term solution: you maintain two outputs, and they can drift apart.

Hydration has a cost too. Shipping and running a large JavaScript bundle can make pages slow to respond, which shows up in INP. The sites we build in our web design and development work send complete HTML and keep JavaScript to what each page needs.

How to test what Googlebot sees

  • URL Inspection in Search Console. View crawled page shows the HTML Google stored at its last crawl. The live test fetches and renders the page now, with a screenshot, the page resources and any JavaScript console messages. Check that your main content, links, canonical and meta robots are all there. A blank or half-built screenshot usually means a script failed or a resource was blocked.
  • Raw HTML against the rendered DOM. Compare view-source (or a curl fetch) with the DOM in your browser’s developer tools. Anything that appears only in the second depends on JavaScript.
  • Crawl twice. Run your crawler with JavaScript rendering off, then on, and compare word counts, links, titles and canonicals by template. The differences are your JavaScript dependencies.

The Rich Results Test renders pages too, which helps when structured data is injected by JavaScript. If you’re moving to a JavaScript framework, treat it as a migration: everything in redesigning or migrating without losing traffic applies.

Beyond Google: crawlers that don’t render

Google renders. Many other crawlers don’t. In our log reviews, many AI crawlers fetch the HTML and never request the JavaScript files a browser would. If your content exists only after rendering, those systems may not see it at all, which makes server-rendered HTML part of AI search optimisation as much as SEO.

Where your important content should live

Only after JavaScript

  • Main copy loaded by client-side API calls
  • Navigation built from click handlers
  • Canonical or noindex set by a script
  • Prices and stock added after load
  • Structured data injected by a tag manager

In the initial HTML

  • Headings and main copy
  • Real links with an href
  • Title, canonical and meta robots
  • Prices and key product details
  • JSON-LD for the main entity
Google can often render the left-hand column eventually. Many other crawlers never will.

None of this means a JavaScript site can’t rank. It means the important parts need to be in place before any script runs, so every crawler starts from the same complete page.


Running a React, Vue or headless build? Our technical SEO team can audit what Googlebot and other crawlers really receive, template by template.

Free toolrobots.txt tester & AI crawler generatorCheck which URLs Googlebot, GPTBot, ClaudeBot and other crawlers may fetch, and build a robots.txt that states your AI crawler policy.Open the tool

Serpevo Strategy Team

Written by Serpevo’s strategy team: technical SEOs, content leads and developers working with brands across the UK, US and Türkiye.

Get a free SEO audit

See what’s holding your search growth back.

Send us your URL. Within 48 hours a senior strategist will send you a personalised audit with your biggest opportunities, completely free.

  • No obligation, no sales pitch
  • Reviewed by a human expert
  • Google, Bing & AI search
Book a call