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.
3. Stateless rendering and cookie rejection
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 system | Executes JavaScript? | Rendering queue delay | Timeout ceiling | Content indexed from client apps |
|---|---|---|---|---|
| Googlebot (WRS) | Yes (headless Chromium) | Hours to days | ~5 seconds | Full hydrated DOM (if under timeout) |
| Bingbot | Yes (headless Edge) | Hours to days | ~5 seconds | Hydrated DOM for high-priority pages |
| OpenAI (OAI-SearchBot) | No (raw HTTP only) | None (instant parse) | Sub-second | Raw server-rendered HTML only |
| PerplexityBot | No (raw HTTP only) | None (instant parse) | Sub-second | Raw server-rendered HTML only |
| Applebot | Limited (low-priority JS) | Days to weeks | ~3 seconds | Primary 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.
1. Links without valid href attributes
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
IntersectionObserverAPI 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:
| Architecture | First-stage HTML content | Rendering queue delay | AI search visibility | Compute cost on server |
|---|---|---|---|---|
| Client-Side Rendering (CSR) | Empty shell (<div id="root">) | High (hours to days) | Zero to poor | Low (static storage) |
| Server-Side Rendering (SSR) | Full HTML body | None (instant) | High | Moderate (per-request compute) |
| Static Site Generation (SSG) | Full HTML body | None (instant) | High | Minimal (prebuilt files at edge) |
| Island Architecture / Hydration | Full static HTML + selective JS | None (instant) | High | Very 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:
- 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.
- 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:
- Click View Tested Page.
- Compare the Screenshot tab against your live production page. Look for missing text blocks, blank banners, or broken navigation menus.
- Check the HTML tab to verify that dynamic content and internal links appear in the DOM.
- 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.
Keep reading
Related posts.
Our services
Services related to this post.
seo digital marketing and smm
Technical, on-page and local SEO
Rank higher for the searches your customers actually type.
Questions
Frequently asked questions
Common questions about javascript seo: how google crawls and renders web apps, answered plainly.
Does Google index JavaScript-rendered content?
Do AI search crawlers like OAI-SearchBot execute JavaScript?
What is the two-wave indexing model in Google Search?
Why do client-side injected meta tags and canonicals fail to update in Google Search?
How can I test whether Googlebot renders my JavaScript content?
Should I move to server-side rendering to fix JavaScript SEO issues?
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.