Skip to content

JavaScript SEO For SaaS: How To Check What Google And AI Crawlers Actually See

Google renders JavaScript late; GPTBot and ClaudeBot never do. Compare raw HTML with the rendered DOM per template, then pick the right fix.

Co-Founder & CEO
Published
Updated
Reading time
22 min read
JavaScript SEO For SaaS: How To Check What Google And AI Crawlers Actually See

Summarise this post with

If your marketing pages need client-side JavaScript to show their content, AI search engines probably cannot see them. GPTBot and ClaudeBot do not render JavaScript, and Googlebot renders it in a second, delayed pass. Put the content in the initial HTML of every page you want ranked or cited.

This applies to any page whose main copy, headings, internal links or metadata are injected by a script after load, which describes most React single-page apps built without a server-rendering layer.

Google's own systems do render JavaScript, so a page can rank in Google and still be missing from ChatGPT or Claude answers. A server-rendered or prerendered page is unaffected, which makes this a per-template problem rather than a per-site one.

If you already suspect AI crawlers are missing your pages, our LLM SEO work covers how crawlers read a site and what they skip. Run the check below first and you will know what to ask for.

How Google Crawls, Renders And Indexes JavaScript

Google can index JavaScript pages. It just does it in a slower, more fragile way than it indexes plain HTML, and that gap is where most of the damage starts.

Googlebot processes a JavaScript page in two passes:

  • Pass one, crawl and queue: Googlebot fetches the HTML the server returns, reads whatever links and content are already in it, and puts the page in a queue for rendering.
  • Pass two, render and index: Google runs the page's JavaScript in its Web Rendering Service and indexes the rendered result. Content that depends on a script can therefore be indexed later than content already in the HTML.
  • The limit that matters: Google's rendering documentation says rendering does not retain local storage, session storage or cookies across page loads, so anything your app only shows after reading saved state will not appear.

The same documentation adds that caching headers may be ignored during rendering. Both points matter for a React app, because many single-page apps quietly rely on stored state to decide what to show. If your pricing table only appears after the app reads a cookie, Google's renderer starts every page load without it.

The delay is the part SaaS teams underestimate. An Onely study from 2022, relayed by Blck Alpaca, found Googlebot took a median of 313 hours to index JavaScript pages against 36 hours for pages without JavaScript. We have not seen the original study and it is an older one, so treat it as indicative and do not promise anyone a number. The shape is simple. Waiting for the render queue is slower than not waiting.

Google does not pretend this is ideal either. Google suggests server-side rendering so that JavaScript-dependent content is accessible to Googlebot. When Google itself recommends sending the content in the HTML, that tells you where the safe default sits.

So Google is the forgiving case. It runs a rendering service and a queue for script-heavy pages. Most of the other crawlers reading your site have none of that, and that is what the next section is about.

What AI Crawlers Do With JavaScript

GPTBot and ClaudeBot do not render JavaScript. If a page's content only exists after a script runs, those crawlers receive an empty shell. Vercel's network data and MERJ's analysis, published in December 2024, found that neither crawler renders JavaScript, so content that exists only after client-side rendering is not visible to them.

Other write-ups agree and extend the list. Averi's analysis says GPTBot, ClaudeBot and PerplexityBot fetch raw HTML and do not execute JavaScript, with Google-Extended as the exception because it inherits Googlebot rendering. A second published list names the same three as non-rendering and says Googlebot, Gemini and AppleBot do execute JavaScript.

CrawlerExecutes JavaScriptWhere The Finding Comes From
GPTBotNoVercel and MERJ study, December 2024
ClaudeBotNoVercel and MERJ study, December 2024
PerplexityBotNoTwo secondary write-ups, undated primary
Google-ExtendedYes, through Googlebot renderingAveri write-up
GooglebotYes, in a delayed second passGoogle's rendering documentation
GeminiYesSecond secondary list
AppleBotYesSecond secondary list

These crawlers are not a rounding error in your server logs. The Vercel and MERJ data shows how much they fetch and how little of it helps them:

  • JavaScript files downloaded: About 11.5% of GPTBot requests and 23.84% of ClaudeBot requests in the Vercel and MERJ data download JavaScript files, yet neither bot executes them.
  • Fetch volume: In the month measured, Vercel's network saw 569 million GPTBot fetches and 370 million ClaudeBot fetches, against 4.5 billion Googlebot fetches in the same period.
  • Wasted requests: Roughly 35% of AI crawler fetches in the same study returned a 404, at 34.82% for ChatGPT and 34.16% for Claude, which is a good reason to keep URLs clean and redirects tidy.

Read the first bullet twice. These bots download your scripts and then do nothing with them. A bundle that loads perfectly in a browser contributes nothing to what the crawler understands about the page.

The volumes are the other point. Against Googlebot, the AI crawlers are smaller, but 569 million and 370 million fetches in a month is not marginal traffic. Which of them matters to you depends on which AI search engines your buyers actually use, and for a B2B SaaS audience the answer is usually more than one.

Crawl access is also the entry condition for everything else in an AI search visibility strategy. No amount of clever content structure helps a page the crawler received empty. If you want help with generative engine optimization on a JavaScript-heavy site, rendering is the first thing we would check.

One warning before you rely on any of this. The crawler data above dates from December 2024. Nothing in our research confirms whether any major AI crawler has started executing JavaScript since, and the list in the table will drift. Do not trust a published list, this one included. Run the check in the next section on your own templates, because that result is the authority for your site.

The Two-Minute Raw HTML Vs Rendered DOM Diagnostic

This is the check we would run first on any React site. View source shows the raw HTML the server returned. The browser inspector shows the rendered DOM after JavaScript has run. Comparing the two shows exactly which content depends on JavaScript, and therefore which content a non-rendering crawler never receives.

Do it once per template, not once per page. A site with hundreds of blog posts has one blog template.

  1. Pick one URL per template. Choose the home page, pricing, a feature page, a blog post, a docs page and a comparison page. Choose pages you want ranked or cited, and skip logged-in screens.
  2. Fetch the raw HTML response. Use view source in the browser, or make a plain request with no JavaScript, for example curl against the URL. Save what comes back.
  3. Open the rendered DOM. In the browser inspector, look at the elements panel after the page has fully loaded. This is what a rendering crawler eventually builds.
  4. Compare the two versions element by element. Check the H1 and other headings, the main body copy, the internal links, the title, the meta description, the canonical tag, any robots directives and the structured data.
  5. Record the result in a table by template. Mark each element as present or missing in the raw HTML, and give the template a pass, partial or fail.
  6. Repeat with JavaScript switched off in the browser. This is a quick, rough sanity test of what a non-rendering crawler would receive. It does not replace reading the raw response, but it is fast and catches the obvious failures.

The decision rule is one sentence. If a heading, paragraph or link appears in the rendered DOM but not in the raw HTML, the page depends on JavaScript for that element, and crawlers that do not render will never see it.

One secondary analysis, relayed by Blck Alpaca, puts the median rendered-versus-raw word count gap on homepages at 13.6% on mobile and 17.5% on desktop. We have not seen the original analysis, so read those figures as a rough guide to the size of the gap and not as a benchmark for your site. Your own pass or fail result matters more than any median.

Keep the results somewhere your team can find them. A spreadsheet works, and so does the audit routine described in our guide to content audit reports from Search Console, which is a sensible home for a record of what each template looked like on each check.

TemplateExample URLHeadings In Raw HTMLMain Copy In Raw HTMLInternal Links In Raw HTMLTitle, Meta And Canonical In Raw HTMLVerdict
HomeYour URL hereYes or noYes or noYes or noYes or noPass, partial or fail
PricingYour URL hereYes or noYes or noYes or noYes or noPass, partial or fail
Feature PageYour URL hereYes or noYes or noYes or noYes or noPass, partial or fail
Blog PostYour URL hereYes or noYes or noYes or noYes or noPass, partial or fail
Docs PageYour URL hereYes or noYes or noYes or noYes or noPass, partial or fail
Comparison PageYour URL hereYes or noYes or noYes or noYes or noPass, partial or fail

How you read the table matters more than filling it in. A pass means the headings, the main copy and the internal links are all in the raw HTML. A partial usually means the shell is there but the body is not, which is the common React pattern. A fail means the raw response is close to empty.

Audit a sample per template at launch, then re-check monthly. New templates and new components are how this problem comes back. If a page is reported missing from search or from AI answers, investigate that one page directly rather than waiting for the monthly pass.

The Page Elements That Break When They Only Exist After Rendering

Not every element fails the same way. A missing paragraph costs you content. A missing canonical or a conflicting robots tag can cost you the page. Use this table as the second half of the diagnostic, once you know which templates differ.

ElementWhat Goes WrongHow To Spot It
H1A crawler that does not render finds no main heading, so the page has no clear topic signal in the raw HTML.Search the raw HTML for the H1 text and find nothing.
TitleThe raw HTML carries a generic or empty title, and the real one is set only after the app loads.Compare the title in view source with the browser tab.
Meta DescriptionThe description exists only in the rendered DOM, so non-rendering crawlers never read it.Search the raw HTML for the meta description tag.
CanonicalA canonical that differs between raw and rendered HTML sends two conflicting signals about the preferred URL.Compare the canonical tag in both versions.
Noindex And NofollowA directive present in only one version leaves crawlers acting on whichever version they read.Compare every robots directive in both versions.
Internal LinksLinks injected by script do not exist in the raw HTML, so a non-rendering crawler cannot follow them to the next page.Count anchor tags in view source against the rendered page.
Structured DataMarkup added by script exists only in the rendered DOM, so a crawler reading raw HTML finds none.Search the raw HTML for the structured data block.

The H1, meta description, canonical, noindex and nofollow can each exist only in the rendered HTML or only in the raw response HTML, and a canonical that differs between the two versions causes a mismatch. That is the pattern to look for in the table above.

The robots case deserves a second look because it is the one that bites silently. When raw and rendered HTML give conflicting robots directives, Google uses the most restrictive one. Picture a page that ships noindex in the raw HTML as a placeholder, and then removes it with JavaScript once the app loads. A crawler that acts on the raw version sees noindex. Google, using the most restrictive directive, sees noindex too. The page you thought you had launched is excluded.

The canonical works the same way, only less dramatically. A crawler can act on either version, so a canonical that points to the wrong URL in one of them gives it a reason to prefer a page you did not intend.

The practical rule is to make the raw HTML the source of truth for anything that controls indexing. Titles, meta descriptions, canonicals and robots directives should be in the response before any script runs. Treat the rendered version as a confirmation, not as the place those tags are created.

React-Specific Failure Patterns That Hide Content

The top-ranking guides explain how Googlebot renders JavaScript and compare rendering modes. Almost none of them tell a React team which of their own habits fail the diagnostic. These are the patterns to look for. This is also the section where the gap between marketing and engineering shows up, which is why the work sits with content engineers and technical SEOs as much as with writers.

Client-side routing, an empty application shell, and metadata set only after hydration are the common ways a React site hides content from crawlers that do not render. Each one below shows up in the diagnostic in a recognisable way.

An Empty Root Div Shipped As The Page Shell

  • Symptom: View source shows a root div and a script tag, and nothing else. The rendered DOM is full of content.
  • Fix: Render the route on the server or at build time so the response carries the real HTML for that page.

Client-Side Routing That Never Produces A Real URL

  • Symptom: Moving between pages changes the view but the address bar shows a fragment, or the app has no distinct URL per page. Crawlers see one page.
  • Fix: Use the History API so every view has its own path, and make sure the server returns the right page for each path.

Navigation Built From Click Handlers Instead Of Anchor Links

  • Symptom: Menus and cards respond to clicks, but view source shows no anchor tags with href values. A crawler has nothing to follow.
  • Fix: Use standard anchor links with real URLs for navigation, and keep click handlers for behaviour only.

Titles And Meta Tags Set Only After Hydration

  • Symptom: The title and meta description in view source are generic, and the real ones appear once React hydrates.
  • Fix: Set the title, description, canonical and robots tags in the server or build output for each route.

A Soft 404 From A Single-Page App

  • Symptom: A missing route shows a "page not found" message in the browser, but the server still returns status 200.
  • Fix: Return a real 404 status for missing routes. Single-page apps need to return meaningful HTTP status codes, because a 200 on a missing route produces a soft 404.

Content That Loads Only On Scroll Or Tab Interaction

  • Symptom: The rendered DOM only gets the full text after you scroll or click a tab, and the raw HTML never contains it.
  • Fix: Put the content in the initial HTML, or expose it at a standard link a crawler can follow, because a crawler that does not scroll or click may never see it.

Is React bad for SEO, then? No. React is a way of building interfaces, and a React page that is server-rendered or statically generated sends full HTML to every crawler. The trouble comes from how the app is shipped. Most of the failures above come down to the same decision, which is leaving the first response empty and filling the page in the browser.

The pattern is easy to spot once you have the table from the diagnostic. If a template fails, ask which of these six it is. If the shell is empty, it is almost always the first one, and the fix is the rendering mode, which the next section compares.

SSR, SSG, Prerendering And Dynamic Rendering Compared

Four options exist, and the diagnostic result tells you which one to ask your engineers about. Server-side rendering builds the HTML on each request. Static generation builds it at deploy time. Prerendering serves a pre-built snapshot to crawlers. Dynamic rendering serves crawlers different output from users, and it is best treated as a bridge for legacy single-page apps rather than a destination.

Google's own position is simple, and Google suggests server-side rendering so that JavaScript-dependent content is accessible to Googlebot. That sits comfortably with the first two options below.

OptionHow It WorksBest ForTrade-Off
Server-Side RenderingThe server builds the full HTML for every request.Public pages whose content changes often, such as pricing or integration directories.Every request costs server work, and the setup is heavier than a static site.
Static GenerationThe HTML is built once at deploy time and served as files.Marketing pages, blog posts and docs that change only when you publish.Content updates need a new build or a revalidation step.
PrerenderingA pre-built snapshot of each page is served to crawlers.A legacy single-page app that cannot be migrated soon.You maintain a second path, and a stale snapshot can drift from what users see.
Dynamic RenderingCrawlers get different output from the output users get.A short bridge while a legacy app is being migrated.It is a workaround with two versions to keep in sync, and it should not be the end state.

Start with static generation or server rendering. Avoid dynamic rendering as a first choice, because it creates two versions of every page and a permanent reason for them to disagree.

In engineering terms, the question to ask is where the HTML is built and when. Server rendering answers "on each request", static generation answers "at deploy", and prerendering answers "ahead of time, for bots only". Give your developer the template, the table above and the diagnostic result, and the conversation is short.

Prerendering is the quickest route when a team cannot rebuild a legacy app this quarter, which is why it keeps appearing. It is a bridge. If you choose it, set a date to move the routes that matter onto a framework that renders on the server.

Which Fix Fits Which Page Type

Do not pick a rendering mode for the site. Pick one per template. The result you want is that every page meant to rank or be cited sends its content in the first response, while screens that are not meant to be indexed stay as they are.

Use a per-route approach:

Page TypeApproachReason
Marketing And Feature PagesStatic generation or server rendering in ReactThese pages are written once and meant to rank, so the full HTML should be in the first response.
Blog Posts And DocsStatic generationContent changes only when you publish, so building it at deploy time is cheapest and fastest.
Pricing And Integration DirectoriesServer renderingPublic data changes often, and the HTML should reflect the current version on each request.
Logged-In App ScreensLeave client-renderedThese screens are not meant to be indexed, so client rendering costs you nothing.
Legacy Single-Page AppPrerendering or dynamic rendering as a bridgeThe app cannot be migrated soon, so a crawler-facing snapshot buys time.

A per-route hybrid is the usual recommendation, with static generation for marketing pages and docs, server rendering for public pages with changing data, and client rendering for authenticated product flows. You can reach the same answer with three questions:

  1. Is the page meant to rank or be cited? If not, such as a logged-in dashboard, leave it client-rendered and stop.
  2. Does its content change per request? If it changes with every visit or every user, use server rendering. If it changes only when you publish, use static generation.
  3. Can the framework already render on the server? If it can, switch the mode on for this route. If it cannot, use prerendering as a bridge while you plan the move.

Notice how little this asks for. Most SaaS sites need to change two or three templates, not the whole site. If your public pages are client-rendered, the marketing pages, the blog and the docs are where the check will fail. The logged-in product does not need to pass it at all. A rebuild is the exception, and the case for one needs to come from something other than rendering.

The result is also what ties search and AI visibility together. The PSOhub case study shows a SaaS site where page-one rankings and AI citations came out of a structured content system. That system only works when the pages it builds are readable by the crawlers in the first place.

If the fix list is longer than one engineer can ship this quarter, the GEO + SEO Growth Programme gives you a team that audits the templates, writes the engineering brief and checks the result in AI answers. Tell us which templates worry you and we will say what we would change first.

Other JavaScript SEO Issues Worth Checking

The raw HTML diagnostic catches the biggest problems. A handful of smaller ones sit beside it, and each is quick to check once you know to look.

  • Lazy loading and infinite scroll: Content loaded on scroll may never be seen by a crawler that does not scroll, so confirm infinite-scroll pages also expose paginated URLs with standard links.
  • Blocked JavaScript and CSS: Blocking script or style files stops Google rendering the page correctly, so confirm your robots rules allow the files a page needs to render.
  • Status codes and soft 404s: Confirm that missing routes return a real 404 status and not a 200 with a "not found" message.
  • The XML sitemap: Confirm every page you want indexed is listed, because the sitemap helps discovery when script-built navigation is weak.
  • Structured data injected by script: Confirm the markup is present in the raw HTML for templates where it matters, as the element table showed.
  • Analytics and tracking: Confirm tracking scripts still fire under your rendering setup, because they can fail under some rendering setups and leave you with a silent gap in the data.

These are all checks, not rebuilds. Most of them take minutes once you have the list of templates from the diagnostic.

The limit of a manual check is that it covers a sample. Once you have checked a sample by hand, a crawler that audits the whole site will find the templates you missed, and Semrush's Site Audit is where we would start for that. Pair it with the template table so a flagged URL maps back to a template and a rendering mode.

Keep the order straight, though. Run the sample check first, because it tells you what to look for in the larger audit. A site-wide crawl without that context returns a long list of issues with no sense of which template causes them. The template result is what turns that list into three engineering tickets.

Briefing Engineering And Proving The Fix Worked

An audit does nothing until an engineer can act on it. Turn the diagnostic into a short brief with three parts. List the affected templates. Name the target rendering mode for each. Write acceptance criteria as checks, so that done means something an engineer can verify.

A good brief reads like this:

  • Affected templates: Marketing pages, feature pages and the blog post template.
  • Target rendering mode: Static generation for marketing pages and the blog, server rendering for the pricing page.
  • Acceptance criteria: The checks below, each of which can be passed or failed from the raw HTML response alone.

For the acceptance criteria themselves, give the engineer four to six checks like these:

  • Heading check: The H1 and the section headings appear in the raw HTML response.
  • Body copy check: The main body copy appears in the raw HTML response without JavaScript running.
  • Link check: The internal navigation links appear in the raw HTML response as anchor tags with real URLs.
  • Metadata check: The title, meta description and canonical tag appear in the raw HTML response.
  • Status check: A missing route returns a 404 status.

Each check is a pass or fail on a single response. That is what makes the brief fair to the engineer and quick for you to confirm.

After the release, prove it worked in three steps:

  1. Re-run the diagnostic. Repeat the raw-versus-rendered check on the affected templates and update the results table.
  2. Confirm the URLs are indexed. Watch indexing for the affected URLs in Search Console.
  3. Monitor a fixed set of prompts. Track whether the pages begin appearing in AI answers, using the same prompts each time. Our write-up of keyword-to-prompt mapping tools covers how to build that prompt set, and the account of how we measured 6,831 AI citations shows the method we use to confirm an effect.

Once the content is visible to crawlers, the next job is making it quotable. Our guide on how to structure content for AI answers picks up where this one stops.

Put a monthly re-check in the calendar. New templates and new components reintroduce the problem, usually without anyone noticing, and the cheapest time to catch a regression is the month it ships. The check takes a few minutes per template, and it is worth far more than the rendering mode you choose on the day.

FAQs

What Is JavaScript SEO?
JavaScript SEO is the work of making sure pages that rely on JavaScript can still be crawled, rendered and indexed. In practice it means checking that headings, copy, links and metadata exist in the HTML a crawler receives, and fixing the templates where they do not, whether the crawler is Googlebot or an AI bot.
Does JavaScript Hurt SEO?
Not by itself. JavaScript hurts SEO when it is the only place your content, links or metadata exist, because crawlers that skip rendering see nothing and Google may index the page later. A page that ships its content in the initial HTML and uses JavaScript for interaction is unaffected.
Is Client-Side Rendering Bad For SEO?
It is risky for any page you want ranked or cited. Googlebot can render client-side pages in its second pass, but GPTBot and ClaudeBot cannot. Keep client-side rendering for logged-in app screens, and use static generation or server rendering for public marketing, blog and docs pages.
Can Google See JavaScript Content?
Usually, but with conditions. Rendering drops saved state between page loads, so content that appears only after reading a cookie or local storage will be missing. Test a representative URL with the diagnostic rather than assuming, and see Google's rendering documentation for the limits.
Can LLM Bots See JavaScript Content?
Not the ones measured. The Vercel and MERJ study found GPTBot and ClaudeBot do not render JavaScript, and other write-ups say the same of PerplexityBot. That data dates from December 2024, so run the raw HTML check on your own templates.
Does Google Treat JavaScript-Based Websites Differently?
Yes, in timing more than in verdict. Content in the initial HTML is available at the first crawl, while script-built content waits for rendering. Nothing suggests a ranking penalty for JavaScript itself, so judge a site by what its raw HTML contains, template by template.
Is There A Rendering Queue, And Does It Affect JavaScript SEO?
Yes, and it matters most on time-sensitive pages. A new pricing page or launch announcement built client-side can be indexed later than a plain HTML page. Put the key copy in the initial HTML for those URLs, and read the queue section above for how the two passes work.
Does Google Discover JavaScript-Heavy Pages Slower?
Discovery relies on links in the HTML, so it slows down when navigation is built from click handlers rather than anchor links. Use standard anchors with real URLs, keep an XML sitemap as a backstop, and expect indexing of script-dependent content to lag behind plain HTML.
Is React Bad For SEO?
No. The rendering mode decides the outcome, not the library. The exception to watch is a React app that ships an empty root div on public routes. If your diagnostic shows that, move those templates to static generation or server rendering and leave the rest alone.
Do I Need To Rebuild My Site To Fix This?
Rarely. Start with the two or three public templates that failed the check and leave logged-in screens alone. The exception is a legacy single-page app with no server rendering option, where prerendering buys time while you migrate the routes that matter most.
Will Fixing Rendering Get My Pages Cited In AI Answers?
No, it only removes a barrier. A crawler that cannot read your page cannot cite it, but the content still has to be worth citing. After the fix, shape it for answer engine optimization, then monitor a fixed prompt set to see whether citations appear.

Not sure whether you have a rendering problem or just slow indexing? Send us the URL of a page that is missing from Google or from AI answers, and we will tell you which one it is.

📑 Disclosure!

Some links in this article are affiliate links. If you sign up through them, The Rank Masters may earn a commission at no additional cost to you. This does not influence which tools we recommend or what we say about them.

Written by

Waqas Arshad
Waqas ArshadCo-Founder & CEO

The visionary behind The Rank Masters, with years of experience in SaaS & tech-websites organic growth.