JavaScript SEO for SaaS
Why SaaS marketing sites built in React go missing: rendering options compared, the failure patterns to test for, and why AI crawlers skip JavaScript.
On this page 9 sections
- How Google’s rendering queue breaks a client rendered marketing site
- CSR, SSR, SSG and ISR: which one for which SaaS page
- The failure patterns that hide SaaS pages from Google
- AI crawlers do not run your JavaScript at all
- How to test rendering in an afternoon
- The argument that works with an engineering team
- The stopgap when a rendering migration is six months away
- What server rendering does not fix
- Start with the diff, not the migration
- Frequently asked questions
The short answer
Googlebot crawls HTML first and renders JavaScript later in a separate, resource-limited queue, so a client-rendered SaaS marketing page can be discovered days before its content is read. Server rendering removes that gap entirely. Use SSG or ISR for marketing pages, SSR where content is personalised, and keep client-side rendering behind the login wall. GPTBot, ClaudeBot and PerplexityBot do not execute JavaScript at all, so client-rendered copy is invisible to answer engines regardless of what Google eventually sees.
Key points before you start
A marketing site ships on React, traffic flattens, and six months later somebody notices that 180 of the 240 pages have never been indexed. The engineering team is confident the site works, because it does work, in a browser. The crawler is having a different experience.
This is the most expensive recoverable mistake in SaaS technical SEO, and it is recoverable. What follows is the version of the argument that survives contact with an engineering team, because it explains the mechanism rather than asserting that JavaScript is bad.
How Google’s rendering queue breaks a client rendered marketing site
Google processes a page in two passes. The first pass fetches the raw HTML, reads links and text that exist in that response, and queues the URL for rendering. The second pass runs the page in a headless Chromium instance, executes the JavaScript, and sends the resulting DOM for indexing.
Those passes are not simultaneous and the second one is not guaranteed to be prompt. Google has said the median wait is seconds rather than days, which is true and also the wrong number to plan against, because medians hide tails. Large sites, low-authority sites and sites that recently shipped thousands of new URLs sit in that tail.
The practical consequences are specific:
- Links that exist only after JavaScript runs are not available to the first pass, so discovery of your deeper pages is delayed or never happens
- Title tags and meta descriptions injected client side may be read on the first pass in their default state, which is why so many React sites show one identical title across every result
- Content changes take two passes to register, so a refreshed page updates in search more slowly than a server-rendered equivalent
- A rendering failure, a script error, a blocked resource, a timeout, produces a page indexed with an empty body and no obvious error anywhere
Assuming Google renders your page the way your laptop does
Googlebot is stateless between page loads. It clears local storage, session storage and cookies each time, it does not accept permission prompts, and it does not run service workers. Any content that depends on a stored value from a previous page, a geolocation prompt or a cached client state simply is not there when the renderer runs. A personalisation layer that reads localStorage returns the empty-state version to Google, permanently.
CSR, SSR, SSG and ISR: which one for which SaaS page
The decision is per page type, not per site. Most SaaS estates should run three of these four at once.
| Method | What the crawler gets on first fetch | Best SaaS use | Main cost |
|---|---|---|---|
| Static generation (SSG) | Complete HTML, served from the edge | Blog posts, feature pages, comparison and integration pages, glossary | Rebuild required when content changes |
| Incremental static regeneration (ISR) | Complete HTML, regenerated on a schedule or on demand | Large programmatic page sets, changelog, docs, pricing with variable data | Framework and host lock-in, cache invalidation complexity |
| Server-side rendering (SSR) | Complete HTML, generated per request | Pages with genuinely per-request content such as localised or logged-in-aware views | Server cost and response time under load |
| Client-side rendering (CSR) | An empty shell plus a bundle | The authenticated product, dashboards, anything behind login | Invisible to non-rendering crawlers, delayed for Google |
The position this guide takes is simple. Every page you allow into the index gets server-rendered or statically generated HTML, and there are no exceptions for the pricing page, the comparison pages or the new product launch microsite. If you are not willing to server render it, do not index it.
Most SaaS marketing sites can get to full static generation without touching the product. The marketing site and the app are usually separate codebases already, and if they are not, splitting them is a smaller project than any of the workarounds people reach for instead. Where the two live and how they should be hosted is covered in the site architecture guide.
Downloadable resource index
Get the resource library index
A downloadable index of templates, checklists, calculators, research guides and category-specific working plans.
The failure patterns that hide SaaS pages from Google
Five patterns account for most of the missing pages in real audits. They are worth testing individually, because each has a different fix and a different owner.
Navigation built as onClick handlers. A design system component uses a div with a click handler and a router push instead of an anchor with an href. Visually identical, functionally identical for users, and completely invisible to the first crawl pass. Mega menus and tabbed product navigations are where this shows up most. The fix is an <a href> under the handler, which costs an hour and recovers whole sections.
Content loaded on interaction. Tabs, accordions and “read more” panels that fetch their content when clicked. Googlebot does not click. If the FAQ block or the feature detail loads on expand, it is not in the DOM at render time and it is not indexed. Content hidden behind a tab but present in the HTML is fine. Content fetched on the tab click is not.
Soft 404s on client-side routes. A single page app returns HTTP 200 for every path, then renders a not-found component. Google receives a successful response for a nonexistent page, flags it as a soft 404, and treats the whole URL class as unreliable. The fix is a real 404 status from the server, or a redirect to a URL that returns 404.
Client-side redirects with no status code. A JavaScript location change carries no HTTP signal. Google follows it, eventually, and treats it more like a temporary redirect than a permanent one, so consolidation of ranking signals is slower and less complete. Any redirect you care about belongs in the server or edge config.
Lazy loading tied to scroll events. Googlebot renders with a tall viewport and does not scroll. Images and sections wired to an intersection observer with a scroll trigger may never fire. Use native loading="lazy" on images below the fold and nothing custom for text content.
180 of 240
Marketing pages left unindexed on a client-rendered React site because navigation links existed only as click handlers, a pattern repeated across audits
Composite audit finding, not a published study
AI crawlers do not run your JavaScript at all
This is the part that changed the calculus in the last two years. Google will eventually render your page. The crawlers feeding answer engines will not.
Vercel published an analysis of AI crawler traffic across its network in December 2024 and found that the ChatGPT and Claude crawlers fetched JavaScript files and executed none of them. Google’s Gemini crawler and AppleBot did render. The implication for a software company is direct: any copy that appears only after hydration is absent from the text those models ingest, so when a buyer asks ChatGPT which tools do X, your page contributes nothing.
That is a distribution problem, not a technical curiosity. Software buyers now shortlist through answer engines before they ever reach a SERP, and the pages that get cited are the ones whose content sits in the initial HTML response.
Check what the non-rendering crawlers actually receive
Run curl with a GPTBot user agent string against your five highest-value pages and read the response body. Then check your robots.txt and any WAF or bot-management rule for blocks on GPTBot, ClaudeBot, PerplexityBot, OAI-SearchBot and Google-Extended. Powered by Search found 68 percent of the 50 SaaS sites it sampled were blocking at least one, usually because a security setting was applied without anyone in marketing knowing. Serving perfect HTML to a crawler you have blocked achieves nothing.
Consultation request
Talk to a SaaS marketing strategist
Tell us about your marketing bottleneck and request a working session. We will confirm availability before scheduling.
How to test rendering in an afternoon
The test is cheap and most teams have never run it properly. You need a terminal, Search Console access and a crawler.
Rendered DOM diff for a SaaS marketing site
- Pick five pages that matter
Homepage, pricing, one feature page, one comparison page, one blog post. These five cover every template that earns money.
- Fetch the raw HTML
Run curl on each URL and save the response. Search each file for a sentence from the middle of the body copy, the H1 text, and the href of a navigation link. Note what is missing.
- Crawl twice with rendering off and on
Run Screaming Frog or Sitebulb with JavaScript rendering disabled, then enabled. Compare total URLs discovered, word counts and internal link counts per page. A gap of more than 10 percent in discovered URLs means your links are JavaScript-dependent.
- Run the live URL Inspection test
In Search Console, inspect each URL, run the live test and open the rendered HTML plus the screenshot. Check the page resources tab for anything Google could not load, which is where blocked CDN paths surface.
- Diff the two DOMs
Save the curl output and the rendered HTML from Search Console, then run a text diff. Everything that appears only in the rendered version is content Google gets late and answer engines never get.
- Repeat with an AI crawler user agent
Fetch the same URLs with a GPTBot user agent and confirm you receive the same HTML and a 200 status rather than a challenge page or a block.
- Write the gap list
One row per missing element, with the component responsible. This is the ticket an engineer can act on, rather than a general complaint that the site has SEO problems.
The technical SEO crawlers comparison covers which tools handle the rendering-on and rendering-off crawl cleanly, and what each one costs at the URL counts a SaaS site actually has.
The argument that works with an engineering team
Framing matters here. Telling engineers that JavaScript is bad for SEO gets you an argument about Googlebot’s capabilities that you will lose, because Googlebot genuinely does render. Frame it as three concrete costs instead.
First, latency to index. Server rendering removes a queue step, so new pages and updates register faster. Any team shipping a programmatic set understands why a queue that processes thousands of URLs slowly is a problem.
Second, crawler diversity. Bing, the AI crawlers, link preview bots, Slack unfurls and every scraper that feeds a directory listing read the initial HTML. Rendering for one of them and not the rest is a narrow bet.
Third, performance. Static HTML from the edge beats a client-rendered page on Largest Contentful Paint in nearly every measurement, which the engineering team already cares about for reasons unrelated to search.
Effort is usually smaller than expected. For a Next.js marketing site that is already using the App Router, the work is often an audit of 'use client' directives and a few component refactors, which is days rather than weeks. For a genuine single page app on a CDN, it is a rebuild, and the honest estimate is four to ten weeks depending on page count. Put that number into the SaaS SEO ROI calculator against your ACV before committing, because at a 4,000 dollar ACV with 30 indexable pages the rebuild may genuinely not pay back inside a year.
The stopgap when a rendering migration is six months away
Sometimes the rebuild is real and the quarter is already committed. There are three defensible interim moves, in descending order of how much I like them.
Move the highest-value pages out of the app first. Pricing, the top five feature pages and every comparison page can often be rebuilt as static files on a separate deployment and routed at the edge, without touching the main application. Twenty pages of static HTML is a week of work and covers most of the commercial surface. Teams on Vercel, Netlify or Cloudflare Pages can do this with a path rule and no framework change.
Prerender at the edge. A worker intercepts bot user agents, serves a cached rendered version and passes everyone else through to the app. Prerender.io productises this, and self-hosted versions run on Cloudflare Workers or a small Puppeteer service. It works today and it will keep working. Google stopped recommending dynamic rendering in 2022 and now describes it as a workaround rather than an approach, mainly because the two versions drift apart over time and nobody notices until a page has been serving stale HTML to crawlers for a quarter.
Ship server-rendered head tags only. The cheapest possible fix: render the title, meta description, canonical, Open Graph tags and the H1 on the server even if the body stays client side. It does not fix the body copy problem and it does fix the duplicate-title problem that makes every result on a React site look identical in the SERP.
Set an expiry date on the stopgap
Every dynamic rendering setup I have seen become permanent has eventually broken quietly. The bot version keeps the old pricing, the old H1 or the old schema, and because nobody views it in a browser, the drift goes unnoticed for months. Put a review date in the ticket and a monthly diff check between the bot response and the user response, or do not ship it.
What server rendering does not fix
Rendering is a precondition, not a strategy. Pages that render perfectly and say nothing new still will not rank. A server-rendered thin page and a client-rendered thin page perform identically, which is to say not at all.
Three specific things server rendering will not do. It will not create search demand for a category that does not exist yet, which is a keyword problem best worked through with a proper keyword map rather than a rendering change. It will not fix internal linking, so pages with crawlable links from nowhere stay undiscovered whether the HTML is served or built. And it will not rescue a programmatic set where every page is a template with three variables swapped, because Google’s response to that is a quality judgement, not a rendering one. The quality bar for those sets is what the programmatic page brief template is for.
There is also a real tradeoff on the other side. Full server rendering on a high-traffic site costs compute, adds an origin dependency, and introduces cache invalidation problems that static generation avoids. Teams that move everything to SSR when ISR would do end up paying for rendering on pages that change twice a year. Default to static, add incremental regeneration where content changes often, and reserve request-time rendering for content that genuinely varies per request.
Start with the diff, not the migration
Run the curl and rendered-HTML comparison on five pages this week. If the body copy is present in the raw response, your rendering is fine and this whole topic is a distraction from content and links. If it is missing, you have found the reason your traffic curve looks the way it does.
From there the sequence is: fix navigation anchors, fix soft 404s, then schedule the rendering migration with a real estimate attached. The sprint module on fixing what blocks indexing runs through that order with checkpoints, and the wider SaaS SEO hub covers what to do once the pages are readable. If you are rebuilding rendering and content at the same time, sequence both against the 90 day SaaS SEO plan so the technical work lands before the publishing volume does, and pair it with technical SEO for SaaS for the indexation rules that come next.
Editable CSV worksheet
SaaS SEO planning worksheet
A practical seo planning worksheet: decisions, owners, evidence and next actions.
Frequently asked questions
Can Google index JavaScript content?
Yes, but on a delay and with conditions. Googlebot crawls the raw HTML first, queues the page for rendering in a headless Chromium instance, then indexes what the render produced. Google has said the median queue wait is seconds, though the tail runs much longer for large or low-priority sites. Content that only appears after a user click or scroll is never rendered, because Googlebot does not interact with pages.
Is React bad for SEO?
React is fine. Client-only rendering is the problem. A React site built with Next.js, Remix, Astro or any framework that emits HTML on the server indexes exactly like a static site. A React single page app served as an empty div plus a bundle gives crawlers nothing to read on first fetch, and that is a build choice rather than a library limitation.
What is the difference between SSR and CSR for SEO?
Server-side rendering produces complete HTML at request time, so the crawler reads the content on the first fetch. Client-side rendering ships an empty shell and builds the page in the browser, so the crawler sees nothing until a separate rendering pass runs. For indexable marketing pages that difference decides how fast you get indexed and whether non-rendering crawlers see you at all.
Do AI crawlers like GPTBot render JavaScript?
Largely no. Vercel's December 2024 analysis of crawler traffic across its network found ChatGPT and Claude crawlers fetched JavaScript files but executed none of them, while Gemini and AppleBot did render. Practically, any content that only exists after hydration will not reach the models that answer software buying questions, which is now a real distribution channel.
Should a SaaS pricing page be server rendered?
Yes, without exception. Pricing is the highest-intent page on a software site, the one most linked to externally, and the one answer engines quote most often when someone asks what a tool costs. Loading the plan table client side means Google reads an empty page on first fetch and ChatGPT reads it that way permanently.
How do I test whether Google can see my JavaScript content?
Fetch the URL with curl and search the response for a sentence from the middle of the page. Then run the live URL Inspection test in Search Console and open the rendered HTML. If the sentence appears in the rendered HTML but not in the curl response, Google will eventually index it and no non-rendering crawler ever will. If it appears in neither, the page has a rendering failure.
Is dynamic rendering still a valid SEO solution?
It works, and Google stopped recommending it in 2022, describing it as a workaround rather than a long-term approach. Services such as Prerender.io serve prerendered HTML to bots and the normal app to users. It is defensible as a stopgap while a proper rendering migration is scheduled, and a liability if it becomes the permanent architecture, because the two versions drift apart.
The saas-marketing.net editorial team Research and editorial
We research, write and maintain every page on this site. The library explains marketing decisions through practical frameworks, explicit assumptions and references. Corrections can be requested through the contact page.
Published September 11, 2026. Last updated .