SEO

JavaScript SEO: how Google crawls and renders web apps

How Googlebot and AI search crawlers handle client-side JavaScript, how the Web Rendering Service processes DOM changes, and how to fix hydration crawl traps.

JavaScript SEO: how Google crawls and renders web apps

Client-side JavaScript frameworks power most modern web work. That choice creates hidden barriers for search bots and AI answer engines. A crawler does not browse your page like a human on a fast desktop. It reads what the server sent, under strict limits.

Search bots work inside tight limits on compute, memory and time. If your key content, links or schema only appears after JavaScript hydrates the page, you risk two outcomes: slow indexing in Google, and total absence from AI search.

This guide covers how Googlebot runs client-side code. It covers why AI search bots skip JavaScript entirely. And it covers how to build dynamic sites that search can still read in 2026.

How Googlebot’s Web Rendering Service works

Google processes JavaScript in a deferred two-stage pipeline.

Googlebot separates crawling from rendering. Running JavaScript takes real computing power. Google fetches billions of pages a day. It cannot spin up a full headless browser on every one of them in real time.

Instead, Googlebot routes URLs through a two-stage pipeline:

Stage 1: The immediate HTTP crawl

When Googlebot discovers a URL, its primary crawler performs a standard HTTP GET request. It downloads the raw server response:

  • HTML markup
  • HTTP response headers (status codes, canonical headers, robots directives)
  • Inline CSS and text content present in the initial payload

At this stage, Googlebot extracts any standard <a href="..."> links found directly in the server HTML. It adds them to its crawl frontier and indexes the raw text for initial keyword matching.

If your page returns an empty shell such as <div id="root"></div> with JavaScript bundle tags, Googlebot sees an empty page during stage one. Run the curl check below to know which case you are in.

Stage 2: The deferred rendering queue

Once the initial HTML is saved, Googlebot places the page into a rendering queue. The Web Rendering Service (WRS) pulls URLs from this queue when compute resources become available.

The WRS uses an evergreen headless Chromium instance to execute scripts, resolve asynchronous API requests, and render the final DOM tree. Once rendering finishes, Googlebot runs a secondary indexing pass over the rendered HTML, extracting dynamically generated links and content chunks.

The key variable is the wait between stage one and stage two. Your site’s authority and your server speed both push that wait around. It runs from a few minutes to several days. On fast-moving news or publishing sites, that wait costs real traffic.

Googlebot rendering constraints and resource limits

Googlebot enforces strict timeouts and disables interactive browser features.

Many teams assume that Chromium means Googlebot runs code like a normal browser. In production it does not. Its headless browser is deliberately hobbled, because running a real browser across billions of pages is expensive:

1. The five-second execution timeout

Googlebot does not wait forever for background API calls or animations. If a component needs more than about 5 seconds to fetch data and paint the page, the WRS kills the script.

Whatever content has rendered before that timeout represents the final state Googlebot indexes. Unrendered sections vanish from the search index.

2. Viewport and headless rendering parameters

Googlebot renders pages with predefined viewport dimensions:

  • Mobile crawler: 412 × 923 pixels with a device scale factor of 2.625.
  • Desktop crawler: 1024 × 768 pixels with a device scale factor of 1.

Googlebot also disables WebGL, GPU acceleration, the camera, the microphone, geolocation and push notifications. Any code that assumes those APIs and has no fallback will throw an error. That error stops all script work on the page.

Googlebot’s WRS keeps no state between page loads. It wipes local storage, session storage, indexedDB and cookies every time.

If your app needs session data or a local storage flag to show product lists, price cards or article text, the crawler sees blank boxes.

Googlebot versus AI search crawlers: the rendering divide

AI search crawlers do not execute JavaScript. They rely entirely on server HTML.

Generative search engines have widened the gap between client-side apps and content bots can find. Google can afford to run headless Chromium at scale. Newer AI crawlers cannot, and they do not try.

Bots such as OAI-SearchBot, PerplexityBot and ClaudeBot are built for fast, cheap text fetching. When one wants a citation snippet, it takes the raw HTML, strips the boilerplate, and reads the text on the spot.

Crawler / Search systemExecutes JavaScript?Rendering queue delayTimeout ceilingContent indexed from client apps
Googlebot (WRS)Yes (headless Chromium)Hours to days~5 secondsFull hydrated DOM (if under timeout)
BingbotYes (headless Edge)Hours to days~5 secondsHydrated DOM for high-priority pages
OpenAI (OAI-SearchBot)No (raw HTTP only)None (instant parse)Sub-secondRaw server-rendered HTML only
PerplexityBotNo (raw HTTP only)None (instant parse)Sub-secondRaw server-rendered HTML only
ApplebotLimited (low-priority JS)Days to weeks~3 secondsPrimary server HTML; partial JS

If your article body or product specs only appear in the browser, Google may still rank the page once the WRS runs. AI search bots will not. They read server HTML directly, so they never see it.

The five most common JavaScript SEO traps

Dynamic routing, missing link tags and injected metadata cause most index failures. In our audits of dynamic apps, the same five structural mistakes keep coming up.

Search engine crawlers do not simulate mouse clicks, tap events, or keyboard inputs. They only discover new URLs by inspecting standard HTML link tags:

<!-- Invisible to search crawlers -->
<button onClick="navigateTo('/pricing')">View Pricing</button>
<span class="link" data-url="/services">Our Services</span>
<a href="javascript:void(0)" onClick="goToPage()">Learn More</a>

<!-- Correct: Fully discoverable by all search bots -->
<a href="/pricing">View Pricing</a>
<a href="/services">Our Services</a>

If your menus, pagination or product grids rely on click handlers with no real <a href="..."> tag, bots have nothing to follow. Those pages drop out of the link graph.

2. Client-side canonicals and meta tag injection

Many single-page apps ship one static index.html. The <title>, <meta name="description"> and <link rel="canonical"> inside it hold placeholders. JavaScript swaps them after the route changes.

In stage one, Googlebot reads the raw tags. If the render queue backs up or the run times out, it indexes the placeholders instead. You then get canonical conflicts, duplicate flags and weak snippets. Check each one with the canonical URL checker.

Always output page-specific meta titles, descriptions, and canonical URLs directly in the initial server HTML payload.

3. Soft 404 errors inside client-side error boundaries

Ask a single-page app for a URL that does not exist and the router often shows a “Page Not Found” panel. The server still replies 200 OK.

Search engines read that as a soft 404. The header says valid, the body says not found. It wastes crawl budget and bloats your index. If a page is gone, send a real 404 or 410 in the first response.

4. Lazy-loading content tied to window scroll events

Lazy-loading helps Core Web Vitals. Doing it with your own scroll listeners breaks crawlers instead.

Googlebot never scrolls. It does grow the page height to fire IntersectionObserver, so use that. Your own scroll handlers (window.addEventListener('scroll', ...)) will likely never run in a headless crawl.

To ensure assets and text render reliably:

  • Use native browser lazy-loading: <img loading="lazy" ...>.
  • Use the modern IntersectionObserver API rather than scroll event listeners.
  • Avoid lazy-loading critical text content, tables, or internal links below the fold.

5. Blocking JavaScript assets in robots.txt

Older sites often blocked /js/ or /assets/ in robots.txt to save bandwidth. Now that stops Googlebot fetching the code it needs to render.

Without your bundle or CSS the WRS renders a broken page. That means layout shifts, hidden text and worse mobile scores. Always let bots fetch CSS and JavaScript.

Rendering architectures: choosing the right approach

Server-side rendering or static HTML delivery delivers the highest search reliability.

To fix render bottlenecks, match your architecture to what search can read:

ArchitectureFirst-stage HTML contentRendering queue delayAI search visibilityCompute cost on server
Client-Side Rendering (CSR)Empty shell (<div id="root">)High (hours to days)Zero to poorLow (static storage)
Server-Side Rendering (SSR)Full HTML bodyNone (instant)HighModerate (per-request compute)
Static Site Generation (SSG)Full HTML bodyNone (instant)HighMinimal (prebuilt files at edge)
Island Architecture / HydrationFull static HTML + selective JSNone (instant)HighVery low (edge caching)

Why Island Architecture excels for search performance

Island architecture sends static HTML and hydrates only the parts that need it: search filters, calculator forms, comment widgets.

This architecture offers two major advantages:

  1. Zero delay for bots: Search engines get complete, semantic HTML straight away, with headings, paragraphs, tables and schema. No render queue, no waiting. Our edge runtime guide covers how to pick a runtime per route.
  2. Better Core Web Vitals: Less JavaScript means a lower Interaction to Next Paint (INP) and Total Blocking Time (TBT). Users notice, and so does mobile search.

Practical diagnostic audit: how to test your site

Compare the raw server response directly against the fully rendered DOM.

Four steps tell you whether bots can index your dynamic pages:

Step 1: Look at the raw server HTML

Run a direct terminal command to inspect what non-rendering crawlers receive:

curl -A "OAI-SearchBot" -sL "https://example.com/your-page" | grep -i "your primary keyword"

If the terminal command returns empty or fails to show your body copy, heading tags, and structured data, your content is trapped behind client-side rendering. AI search bots and stage-one crawlers cannot see it.

Step 2: Use Google Search Console URL Inspection Live Test

Open Google Search Console, input the target URL into the URL Inspection tool, and click Test Live URL:

  1. Click View Tested Page.
  2. Compare the Screenshot tab against your live production page. Look for missing text blocks, blank banners, or broken navigation menus.
  3. Check the HTML tab to verify that dynamic content and internal links appear in the DOM.
  4. Review the More Info tab for JavaScript console errors or resources that Googlebot could not load due to timeouts or robots.txt blocks.

Step 3: Audit server logs for crawler status codes

Examine your web server access logs to confirm that Googlebot and AI crawlers receive clean HTTP 200 responses with low Time to First Byte (TTFB).

Look for patterns where search bots encounter 429 Too Many Requests or 503 Service Unavailable errors. When client-side apps make dozens of internal API requests per page render, bot visits can inadvertently trigger rate-limiting layers, cutting off the crawl before rendering completes.

For a comprehensive review of crawl efficiency and server capacity management, reference our crawl budget optimization guide.

Step 4: Validate structured data and snippets

Ensure your structured data survives hydration without parsing errors. You can evaluate your page structure and AI snippet clarity with our free AIO Inspector tool, validate JSON-LD syntax with our Schema Markup Generator, and test search snippet display using our Meta Tag Previewer.

For large web applications migrating from client-side frameworks to server-rendered architectures, a thorough technical SEO audit helps map out route redirects, status code handling, and crawl architecture.


Frequently asked questions

Does Google index JavaScript-rendered content?

Yes, with one big caveat. Googlebot hands the page to its Web Rendering Service, where a headless Chromium runs your JavaScript. That step is expensive and sits in a queue, and it runs under tight timeouts.

Do AI search crawlers like OAI-SearchBot execute JavaScript?

No. Bots like OAI-SearchBot, PerplexityBot and Common Crawl are plain HTTP fetchers. They carry no browser engine. They read the raw HTML your server sent. Anything that only appears after JavaScript runs is invisible to them.

What is the two-wave indexing model in Google Search?

The two-wave model means Googlebot indexes raw HTML and grabs static links first. It queues the page for rendering later, when compute frees up. Google has sped this up, but the gap between fetch and render still shapes how dynamic apps perform.

Why do client-side injected meta tags and canonicals fail to update in Google Search?

Googlebot reads meta tags and canonicals in the first wave. If JavaScript swaps them after hydration, a failed or delayed render leaves the wrong values indexed. That is where indexing conflicts come from.

How can I test whether Googlebot renders my JavaScript content?

Use the Google Search Console URL Inspection tool and run a Live Test to view the rendered HTML and screenshot. Additionally, test with a raw cURL command to see what search bots without browser engines receive in the initial server response.

Our services

Services related to this post.

Questions

Frequently asked questions

Common questions about javascript seo: how google crawls and renders web apps, answered plainly.

Does Google index JavaScript-rendered content?
Yes, but with caveats. Googlebot sends pages to its Web Rendering Service where a headless Chromium instance executes JavaScript and renders the DOM. However, this rendering process is computationally expensive, deferred to a queue, and subject to strict execution timeouts.
Do AI search crawlers like OAI-SearchBot execute JavaScript?
No. Most generative AI search crawlers (including OAI-SearchBot, PerplexityBot, and Common Crawl) operate as high-throughput HTTP fetchers without full browser rendering engines. They only extract the raw server-rendered HTML. Content rendered purely client-side is completely invisible to them.
What is the two-wave indexing model in Google Search?
The two-wave indexing model describes how Googlebot first indexes raw HTML and extracts static links immediately, then queues the page for JavaScript rendering at a later time when compute resources become available. While Google has optimized this pipeline, the delay between fetch and render remains a real factor for dynamic web apps.
Why do client-side injected meta tags and canonicals fail to update in Google Search?
Googlebot parses initial meta tags and canonical URLs during the first crawl wave. If client-side JavaScript modifies or injects canonical tags after hydration, Googlebot may index the raw pre-hydration state if rendering fails, times out, or gets deprioritized, leading to indexing conflicts.
How can I test whether Googlebot renders my JavaScript content?
Use the Google Search Console URL Inspection tool and run a Live Test to view the rendered HTML and screenshot. Additionally, test with a raw cURL command to see what search bots without browser engines receive in the initial server response.
Should I move to server-side rendering to fix JavaScript SEO issues?
Only if the content genuinely needs to be in the initial HTML. Astro, SvelteKit or a hybrid setup that ships real text in the response usually beats rewriting a React SPA. Client-side islands are cheaper to build and work fine when the critical content already renders on the server.

Was this article helpful?

One tap. No email, no signup.

WebGrow24 services

Need help putting this into action?

Fixed scope, 48-hour turnaround. Senior team. Outcome-led.

Friday growth tip

One practical growth tip every Friday. No spam, unsubscribe anytime.

Chat with us — Friday growth tip