Skip to content
SERPCompass

Content that only JavaScript shows

Google can index content that JavaScript adds: it renders pages in a recent version of Chromium, though a page can wait in a queue first. Many AI crawlers read only the HTML your server sends: a December 2024 study by Vercel and MERJ found that OpenAI's, Anthropic's and Perplexity's crawlers did not run JavaScript. Put your main text in that HTML with server-side rendering or pre-rendering.

Last updated 29 October 2026, when every step was checked against the vendor's documentation.

Why it matters

Google handles a JavaScript page in three phases: it crawls the HTML, renders the page in an evergreen version of Chromium when its resources allow, and indexes the rendered result. A page can wait in the rendering queue for a few seconds or longer, and Google says content that is not in the rendered HTML cannot be indexed. Its own advice is that server-side rendering or pre-rendering is still a great idea, because it makes the site faster and because not all bots can run JavaScript.

That matters most for AI answers. In a study published on 17 December 2024, Vercel and MERJ reported on AI crawler traffic across Vercel's network and found that none of the major AI crawlers they observed rendered JavaScript: OpenAI's, Anthropic's, Meta's, ByteDance's and Perplexity's. ChatGPT's and Claude's crawlers downloaded script files without running them. Google's Gemini, which uses Googlebot's infrastructure, and Applebot did render pages. A page whose text appears only after scripts run gives crawlers that read the HTML next to nothing to read or quote.

The fix is to send the finished text in the HTML. web.dev describes the choices: server-side rendering builds the HTML on the server, static rendering builds it ahead of time at build time, and hydration then runs scripts to make that HTML interactive. Google recommends these, and calls dynamic rendering, serving crawlers a pre-rendered copy while visitors get the JavaScript app, a workaround and not a recommended solution.

SERPCompass rates both findings as warnings. It reads every page as your server sends it, which is what a crawler that does not run JavaScript receives, so a page listed here is one those crawlers see almost empty.

The audit findings this guide fixes: Content that needs JavaScript to appear; Main content added by JavaScript (rendered sample).

Find every page with this problem

Run the free scan on your domain. On every page it crawls that is not marked noindex, it reads the HTML as your server sends it and flags pages that look like an empty JavaScript app: fewer than 150 words on the whole page, plus either an empty app container (an element with the id root, app, __next, __nuxt, ___gatsby, svelte or q-app holding fewer than 30 words, or a framework marker such as data-reactroot or ng-version) or a <noscript> message asking visitors to enable JavaScript. An audit run with JavaScript rendering also opens each page in a browser and compares the words: it flags a page when the browser shows at least 50 words, more than one and a half times as many as the HTML as served and over 100 more, and reports that page under this measurement only. The scan shows your most urgent findings with the number of pages each affects; the Gap Report and a SERPCompass workspace list the pages. To check one page yourself, view its source (Ctrl+U, or Cmd+Option+U on a Mac): that is the HTML before any script runs. Text in the HTML only helps if crawlers may fetch the page, so the free AI crawler checker shows which search and AI crawlers your robots.txt lets read your home page.

Scan my site free Use the free checker

How to fix it on your platform

How much JavaScript slows a page down, and canonicals, redirects or noindex rules that scripts change, are not covered here.

WordPress

  1. WordPress builds pages on the server. The block editor stores a static block's HTML with the post, and generates a dynamic block's HTML on the server when the page is requested, so the text of ordinary posts and pages arrives in the HTML.
  2. If the scan flags a WordPress page, something on it fills the page in the browser instead. View the page's source, find where the missing text should be, and identify the block, widget or plugin that fills that spot with a script.
  3. Replace it with ordinary blocks, or ask its developer for a way to output the content on the server.

Source: Static or dynamic rendering of a block (WordPress block editor handbook).

Shopify

  1. Shopify's theme guidance is to render the initial view of every page with Liquid and HTML, not with client-side frameworks, so the server sends complete HTML.
  2. If a page of your store is flagged, find the section or app that builds its content with JavaScript after the page loads, and move that content into Liquid.
  3. Where content has to change after the page loads, Shopify recommends its Section Rendering API, which fetches HTML rendered on the server, instead of building the content with JavaScript.
  4. If you need a JavaScript framework, Shopify points to server-side rendering with Hydrogen rather than a client-side framework on a Liquid storefront.

Source: Render essential content in Liquid and HTML, not JavaScript (Shopify.dev).

Wix

  1. Wix renders your pages on its servers and sends them as HTML, so what you add in the editor reaches crawlers that do not run JavaScript.
  2. If you add content with Velo code, set it inside $w.onReady(): Wix renders what runs there on the server. For an asynchronous query, return its promise from onReady() so the server waits for the result. Content a dataset loads, rather than code, is included anyway.
  3. To see what crawlers get, view the page's source and check that your headings, body text, links and image alt text are there.

Sources: SEO best practices (Wix developer documentation); Viewing your page's source code the way search engines do (Wix Help Center).

React with Next.js

  1. React's documentation recommends starting new apps with a framework such as Next.js or React Router; these frameworks can generate static pages and render on the server route by route.
  2. Next.js calls an app served from one HTML file, with every route rendered by JavaScript in the browser, a strict single-page application, and has migration guides from Create React App and Vite.
  3. Next.js prerenders client components during next build. Do not load content you want read with next/dynamic and ssr: false: that turns prerendering off and loads the component only in the browser.
  4. For a site with no server, a static export (output: 'export' in next.config) writes an HTML file for each route.

Sources: Creating a React app (React documentation); How to build single-page applications with Next.js (Next.js documentation).

Vue with Nuxt

  1. Nuxt renders on the server by default: the server sends the page as HTML and the browser then makes it interactive.
  2. Look in nuxt.config.ts for ssr: false, which renders the whole app in the browser only. Remove it, or set ssr: false only for sections that need no indexing, such as /admin/**, in routeRules.
  3. To serve static HTML, prerender pages with nuxt generate, or with prerender: true for routes in routeRules.

Source: Rendering modes (Nuxt documentation).

Angular

  1. Angular renders in the browser by default (RenderMode.Client), which its documentation says may hurt search engine optimization.
  2. Add server-side rendering to an existing project with ng add @angular/ssr, or start a new one with ng new --ssr.
  3. In the server routes, give pages you want indexed RenderMode.Prerender, which writes static HTML for each route at build time, or RenderMode.Server, which renders each request on the server.

Source: Server and hybrid rendering (Angular documentation).

Plain HTML

  1. Write the main text, headings and links into the HTML your server sends, and use scripts to add behaviour on top, not to insert the content.
  2. To see what a crawler that skips JavaScript gets, open Chrome's developer tools, press Control+Shift+P (Command+Shift+P on a Mac), run Disable JavaScript and reload. It stays off in that tab while the developer tools are open.
  3. To see what Google renders, open the URL Inspection tool in Search Console or the Rich Results Test and look at the rendered HTML.
  4. Make links <a> elements with an href: Google finds links only that way.

Sources: Understand the JavaScript SEO basics (Google Search Central); Disable JavaScript (Chrome for Developers).

Examples

  • Bad
    <body>
      <div id="root"></div>
      <noscript>You need to enable JavaScript to run this app.</noscript>
      <script src="/static/js/main.js"></script>
    </body>

    The HTML the server sends: an empty container and a request to enable JavaScript. A crawler that does not run scripts sees no content.

  • Good
    <body>
      <div id="root">
        <h1>Drain cleaning in Leeds</h1>
        <p>Same-day drain unblocking at fixed prices, seven days a week.</p>
      </div>
      <script src="/static/js/main.js"></script>
    </body>

    Rendered on the server: the text is in the HTML, and the script makes the page interactive.

  • Bad
    42 words in the HTML as served, 1,180 words once JavaScript ran

    What an audit with JavaScript rendering reports: nearly all the text exists only after scripts run.

  • Bad
    const Reviews = dynamic(() => import('./reviews'), { ssr: false })

    In Next.js, this loads the reviews only in the browser, so they are not in the HTML.

Common mistakes

  • Checking only in a browser. It runs JavaScript, so the page looks complete: view the source or turn JavaScript off to see what crawlers that skip it receive.
  • Relying on Google's rendering alone. Google renders JavaScript, and still says server-side rendering or pre-rendering is a great idea because not all bots can run it.
  • Serving crawlers a separate pre-rendered copy as the long-term fix. Google calls dynamic rendering a workaround and recommends server-side rendering, static rendering or hydration.
  • Loading the main text, product details or reviews from an API after the page loads, when they could be in the HTML.
  • Blocking script files in robots.txt: Google does not render JavaScript from files or pages robots.txt blocks.
  • Putting noindex in the HTML and removing it with JavaScript. Google may skip rendering when it finds noindex, so the page stays out.

Questions and answers

Can Google see content added by JavaScript?

Yes, as a rule. Google renders pages with an evergreen version of Chromium and indexes what is in the rendered HTML, though a page can wait in its rendering queue before that happens. Content that is not in the rendered HTML cannot be indexed. The URL Inspection tool in Search Console shows the HTML Google rendered for a page.

Do AI crawlers like GPTBot and ClaudeBot run JavaScript?

A study Vercel and MERJ published in December 2024 found that none of the major AI crawlers it observed rendered JavaScript, including OpenAI's, Anthropic's and Perplexity's; some downloaded script files without running them. Google's Gemini uses Googlebot's infrastructure and Applebot uses a browser-based crawler, so both render pages. To be read by all of them, put your content in the HTML your server sends.

Is client-side rendering bad for SEO?

Not for Google by itself, since Google renders JavaScript, but it adds a wait before Google sees the content, and crawlers that do not run JavaScript see an empty page. Angular's documentation says client-side rendering may negatively affect SEO. Server-side rendering, static rendering or hydration avoid the problem.

Is dynamic rendering still recommended?

No. Google calls dynamic rendering a workaround and not a recommended solution, because it adds complexity and needs more resources, and recommends server-side rendering, static rendering or hydration instead. Google does not generally treat it as cloaking, as long as crawlers and visitors get similar content.

How do I see what a crawler that skips JavaScript sees?

View the page's source with Ctrl+U, or Cmd+Option+U on a Mac: that is the HTML before scripts run. Or open Chrome's developer tools, run the Disable JavaScript command from the Command Menu and reload. If your headings and main text are missing, those crawlers cannot read them.

Start with a free scan of your site.

Two to five minutes, no account. You get your three most urgent findings, and the exact count of everything else.

Free, 2–5 minutes, no account needed.