# JavaScript SEO for SaaS Sites Built on React and Next.js

> Which pages on a React or Next.js SaaS site need server rendering, how to test what Googlebot and AI crawlers receive, and the bugs that block indexing.

Source: https://comppound.com/blog/javascript-seo-for-saas-sites-built-on-react-and-nextjs (published 2026-10-30)

JavaScript SEO is the work of making sure search engines and AI crawlers can read pages that rely on JavaScript to show their content. For a SaaS site built on React or Next.js, the practical answer is short: every page you want found should send its main content, title, canonical and links in the first HTML response. Keep client-side rendering for the logged-in app.

Google can run JavaScript, so a client-rendered page is not automatically invisible. Other crawlers are less forgiving, and [Google's own JavaScript SEO documentation](https://developers.google.com/search/docs/crawling-indexing/javascript/javascript-seo-basics) still calls server-side rendering or pre-rendering a great idea because it is faster for people and crawlers and because not all bots can run JavaScript.

## What actually happens when Googlebot meets your React page

Google handles a JavaScript page in three phases: crawling, rendering and indexing. It fetches the URL and reads the raw HTML for links. Pages that return a 200 status go into a queue for rendering, where a headless version of Chromium runs the JavaScript. Google then reads the rendered HTML and uses it for indexing.

Two consequences matter for a SaaS team. Anything present only after rendering arrives later than anything in the raw HTML, so a new feature page may take longer to be understood. And anything that breaks during rendering, such as a blocked API call or an error thrown by a third-party script, can leave Google with an empty shell.

Say you run a 30-person workflow SaaS with a marketing site built as a single-page React app. The homepage HTML is one empty div and a script tag. Your integrations directory loads its list from an API after the page mounts, and each integration opens through a click handler. Google may eventually render the directory, but the crawler sees no links in the raw HTML, and a crawler that doesn't run JavaScript sees nothing at all.

## Why AI crawlers raise the stakes for client-side rendering

Google has years of rendering infrastructure behind it. You shouldn't assume the same of every other system that reads your site. Crawlers vary in whether they execute JavaScript, and the behavior of any given one can change without notice, so a page that depends on it is betting on a detail you don't control.

The safe design doesn't require you to know the answer. If the content is in the initial HTML, every crawler gets it, whatever it does with scripts. That is why rendering choice belongs in the same conversation as [how AI systems choose which SaaS pages to quote](/blog/how-ai-search-citations-pick-saas-blogs-to-quote): a page can't be cited for text the crawler never received.

## Choosing a rendering mode for each page type

The useful decision is per page type, because a SaaS site is several kinds of site at once. Next.js lets you mix modes in one project, so you rarely have to pick just one.

| Page type | Rendering mode | Why |
| --- | --- | --- |
| Homepage, features, pricing, comparison pages | Static generation | Content changes on deploy, and the page is fast and complete for every crawler |
| Blog and glossary | Static generation with periodic revalidation | New posts appear without a full rebuild |
| Integration and template directories | Static generation with revalidation, or server rendering | Large sets of pages that must be crawlable and fresh |
| Public docs | Static generation | Often a strong source of long-tail traffic, and often the part left client-rendered |
| Logged-in app | Client rendering, kept out of the index | Nobody searches for a dashboard, and these URLs waste crawl attention |

Next.js is a server-rendering framework, but using it doesn't settle the matter. A page marked as a client component that fetches its content in an effect still ships a loading state as its HTML. The test is what the response contains, whatever the framework says on the box.

## How to check what a crawler receives

Run three checks on one page of each type, in this order. The first takes seconds and catches most problems.

1. **Read the raw response.** Run `curl -s https://example.com/pricing` and search the output for a sentence from the page, the title and the canonical. If the sentence isn't there, the content depends on JavaScript.
2. **Inspect the rendered page.** In Search Console, use the URL Inspection tool and view the crawled page. Compare its HTML and screenshot with what you see in a browser, and look at which resources failed to load.
3. **Crawl with JavaScript off, then on.** Point a site crawler at the site both ways and compare the URL counts. Pages that appear only with rendering on are reachable only through JavaScript.

Repeat this after framework upgrades and redesigns. Rendering regressions tend to arrive quietly, when someone moves a data fetch or wraps a section in a client-only component to fix an unrelated bug.

## The JavaScript bugs that cost SaaS sites indexed pages

Most damage comes from a short list of specific mistakes, and Google's documentation addresses several directly.

**Noindex set in the HTML and removed by JavaScript.** Google may skip rendering when it sees a noindex tag, so a script that removes it may never run. If you want a page indexed, leave noindex out of the original code. This one often appears when a staging setting leaks into a template.

**Canonicals injected or changed on the client.** Google can pick up a canonical added during rendering, but it recommends HTML. If JavaScript touches it, it must match the one in the HTML or be the only one. A framework head component plus a third-party SEO script can easily produce two different tags on one page.

**Links that aren't links.** A clickable `div` with an `onClick` handler gives a crawler nothing to follow. Use a real `<a>` element with an `href`. The Next.js `Link` component renders one, so the risk is mostly in custom cards and buttons. Our guide to [an internal linking strategy for a SaaS blog](/blog/an-internal-linking-strategy-for-a-saas-blog) covers where those links should point.

**Hash-based routes.** URLs like `/#/pricing` rely on fragments, which Googlebot can't reliably resolve into separate pages. Use the History API, which modern React routers do by default.

**Soft 404s in single-page apps.** With client-side routing, a missing page often returns a 200 status and a friendly "not found" message. Google suggests redirecting with JavaScript to a URL that returns a real 404, or adding a noindex tag to the error view. In Next.js, the `notFound()` function returns a proper 404 from the server, which is better than either workaround.

**Content behind interaction.** Text that loads only after a click on a tab or an accordion may never be fetched. It is fine to hide content that is already in the HTML. It is a problem when clicking is what fetches it.

**Blocked resources.** If `robots.txt` blocks the scripts or API routes a page needs to render, Google renders a broken page. Check the blocked resources in URL Inspection before assuming a page is thin.

## A migration order that doesn't stall product work

If your marketing site is fully client-rendered, you don't need to rebuild everything at once. Fix the pages closest to revenue first, then move outward.

Using the workflow SaaS from earlier, the order would be pricing and the top feature pages, then comparison pages, then the integrations directory, then the blog and docs. After each group ships, run the three checks above and watch those URLs in the Search Console page indexing report. If the rendering work changes URLs, treat it as a migration and map redirects before launch.

A small change can be enough for the first step. Moving the integrations fetch from a client effect into a server component means the list and its links arrive in the first response, with no visible change for the visitor. Measure the outcome over weeks and by page group, because Google needs time to recrawl and a single day's numbers won't tell you much.

If you would rather have someone own this alongside the content plan, [see what SEO Growth includes](/services/seo-growth).

## Keep reading

- [How to Run an SEO Audit for SaaS Sites, Step by Step](/blog/seo-audit-for-saas-sites-a-step-by-step-process)

## Frequently asked questions

### Can Google index JavaScript content?

Yes. Google renders pages with a headless version of Chromium and indexes the rendered HTML. Rendering can still fail or be delayed, so content you care about is safer in the initial HTML response.

### Is Next.js automatically good for SEO?

No. Next.js makes server rendering and static generation available, but a page can still ship an empty loading state if its content is fetched on the client. Check the raw response of each page type to be sure.

### Do I need server-side rendering for my logged-in SaaS app?

Generally no. Dashboards and account pages have no search value, so client rendering is fine there. Put the effort into public pages such as pricing, features, comparisons, integrations, docs and the blog.

### How do I test whether a page depends on JavaScript?

Fetch it with curl or view the page source and search for a sentence from the body. If the sentence is missing, it depends on JavaScript. Then use URL Inspection in Search Console to see what Google rendered.

## About Comppound

Comppound runs SEO and AI search for B2B SaaS companies: a person sets the plan and reviews samples and every high-risk page, and AI speeds up production.

- [Check a page free](https://comppound.com/tools/seo-checker): Check one of your pages before you change anything else
- [See pricing](https://comppound.com/pricing): Want the content written, reviewed and published for you?
