Technical SEO
JavaScript SEO: How Google Renders JS, and How to Make It Crawlable and Citable
I write code and I do SEO, and JavaScript SEO is where those two jobs collide most often. A framework decision made for developer ergonomics — render this on the client, hydrate that after load — quietly decides whether Google, and the AI engines now sitting on top of it, can see your content at all. This guide is the umbrella over the framework-specific spokes in the SEO for engineers series: what every JavaScript stack has in common, before the Astro, React, or Next.js particulars enter the picture.
JavaScript SEO is the practice of making content that a browser assembles with JavaScript equally visible to the crawlers that index and cite it — ensuring the HTML a bot receives, before any script runs, already contains the text, links, and metadata that decide how the page ranks and whether an AI engine can quote it.
Key takeaways
- Google renders JavaScript — eventually. Google processes pages in three phases — crawl, render, then index — and rendering is queued to a later pass once resources allow.
- The “Google can’t handle JS” era is over. A Vercel and MERJ study found 100% of indexable HTML pages were rendered, at a median of 10 seconds after the initial crawl.
- AI crawlers are the new constraint. GPTBot, OAI-SearchBot, ClaudeBot, and PerplexityBot read raw HTML as text and do not run a headless browser, so client-rendered content is effectively blank to them.
- Most JavaScript SEO failures are structural, not exotic. Content stuck behind hydration, links that aren’t real
<a href>elements, and markup injected after the crawl snapshot cause the majority of problems I see. - Crawlable is the prerequisite for citable. The same raw-HTML content Google indexes first is the only content an AI engine can extract, which is why JavaScript SEO now feeds GEO, not just rankings.
How Google actually processes JavaScript in 2026
Start with the mechanism, because most JavaScript SEO advice is downstream of misunderstanding it. Google’s own documentation is explicit that Googlebot processes pages in three phases: crawling, rendering, and indexing. In the first phase it fetches your raw HTML — exactly what the server returns before any script executes — and reads whatever links, text, and metadata are already there.
Rendering is a separate, later step. Google queues pages with a 200 status for rendering, and once its resources allow, a headless instance of an evergreen Chromium executes the JavaScript and produces the final DOM. Only then does the rendered content become eligible for indexing. This is the “two waves” model: raw HTML first, rendered HTML second.
For years the received wisdom was that this second wave was slow, unreliable, and something to fear. That is no longer true, and the best data on it is aimed squarely at engineers. Across more than 100,000 Googlebot fetches, the Vercel and MERJ study found that Google rendered 100% of indexable HTML pages, including pages with complex client-side interactions, with no measurable penalty for JavaScript complexity.
The delay is smaller than the folklore too. That same analysis put the median crawl-to-render gap at 10 seconds, with the 25th percentile inside four seconds — a queue, not a black hole. So the honest 2026 picture is this: Google can and does render your JavaScript for traditional Search. JavaScript SEO is no longer a fight to get indexed by Google at all; it is a fight over what happens in that first raw-HTML wave, because that wave is what everything else now depends on.
The catch: AI crawlers don’t render your JavaScript
Here is the shift that makes JavaScript SEO urgent again rather than solved. Googlebot renders. The crawlers behind AI answers largely do not. GPTBot and OAI-SearchBot (ChatGPT), ClaudeBot (Claude), and PerplexityBot (Perplexity) fetch your HTML and parse it as text — they do not spin up a headless browser and wait for hydration the way Googlebot does. Whatever your JavaScript adds after that fetch is, to them, content that never existed.
You can confirm this on any URL in about ten seconds. Ask for the page as an AI crawler would, and read what actually comes back:
curl -sA "GPTBot" https://example.com/your-page | grep -i "<h1"
An AI crawler never runs your JavaScript, so that raw HTML is the whole story for ChatGPT and Perplexity. If your H1 and body copy are in the output, every class of bot can read the page. If the response is a shell — a <div id="root"> and a script tag — you have found the problem: the content is assembled client-side, and the engines that never execute JavaScript see nothing to quote.
This is where JavaScript SEO stops being about rankings and becomes about citations. The technical fundamentals feed directly into whether an AI engine can source you, and the stakes are concentrated in a small number of surfaces.
Read those two numbers together. Ahrefs found that AI Overview citations skew heavily toward pages already ranking in Google’s top 10, so ordinary crawlability and indexing are the entry ticket to citation. And because Previsible’s 2026 report puts ChatGPT at 92.4% of standalone LLM referral traffic, a page that only exists after hydration forfeits the single largest AI channel — one whose crawler never runs your scripts. That is the crawlable-to-citable arc in one sentence: fix the raw HTML, and you serve Google’s index and the AI engines at the same time.
Client-side vs server-side rendering: a JavaScript SEO decision snapshot
The lever that decides all of this is your rendering strategy — which is a per-template engineering choice, not a site-wide one. I go deep on the trade-offs and the per-template rendering audit in rendering strategies for headless architecture, so here I will keep it to the JavaScript SEO decision at altitude.
- Server-side rendering (SSR): the server runs the JavaScript per request and ships complete HTML. The first raw-HTML wave already contains your content, so both Googlebot and AI crawlers see it immediately.
- Static site generation (SSG): pages are pre-rendered to HTML at build time and served from a CDN. This is the strongest posture for content that updates on an editorial cadence — no render ambiguity, nothing for a bot to wait on.
- Incremental static regeneration (ISR): SSG’s crawler-visible HTML with the ability to regenerate individual pages without a full rebuild. Ideal for large libraries where most pages are stable.
- Client-side rendering (CSR): the server returns a shell and the browser assembles the page. Googlebot renders it in the second wave; AI crawlers do not render it at all. Reserve CSR for genuinely app-like, non-ranking views.
One workaround you can skip: dynamic rendering. Google now states plainly that “dynamic rendering was a workaround and not a long-term solution” and recommends server-side rendering, static rendering, or hydration instead. The framework-specific mechanics of getting your content into that first wave live in the spokes — Astro SEO, React SEO, and Next.js SEO — each grounding this decision in one stack’s defaults and APIs.
The JavaScript SEO failure modes that actually cost rankings
In practice, JavaScript SEO problems are not a long tail of edge cases. The same handful of failure modes account for nearly everything I find on a rendering audit, and each has a concrete check.
1. Primary content stuck behind hydration. The classic CSR shell: the server returns <div id="root"></div> and the article body only appears after the bundle runs. Googlebot may catch it on the second wave; AI crawlers never will. The curl -A "GPTBot" test above is the fastest way to catch it — if your H1 and body copy aren’t in the response, the content is arriving too late.
2. Links that aren’t real links. Client-side routers love onClick handlers that navigate via JavaScript, which never produce a crawlable URL. Google is unambiguous here: it can only discover links that are <a> elements with an href attribute, per its JavaScript SEO documentation. A <div onClick> is invisible as a link even to Googlebot.
// Invisible to crawlers — there is no href to follow
<div onClick={() => router.push('/pricing')}>Pricing</div>
// Crawlable — a real anchor the bot can queue and follow
<a href="/pricing" onClick={handleClientNav}>Pricing</a>
3. Content gated behind interaction. Tabs, accordions, “load more” buttons, and infinite scroll that only fetch their content on click or scroll hide that content from the crawl. A bot does not click. If it matters for ranking or citation, render it into the initial HTML and enhance with JavaScript, rather than the other way around.
4. Blocked scripts in robots.txt. If you disallow the JavaScript or CSS that renders the page, you prevent Googlebot from rendering it in the second wave — you have manufactured a blank page. This is a surprisingly common self-inflicted wound after a “tidy up robots.txt” ticket.
User-agent: Googlebot
Disallow: /_next/static/
Disallow: /assets/js/
5. JavaScript redirects and soft 404s. Redirecting with window.location after the page loads, or rendering an empty “not found” state with a 200 status, both confuse crawlers. Server-side 301/302 responses and real 404 status codes are unambiguous; their client-side imitations are not.
6. Hydration mismatches. When the server-rendered HTML and the client’s first render disagree, frameworks can discard the server markup and re-render on the client — momentarily reproducing the CSR problem for anything caught in that flash. Keeping server and client output identical is a correctness issue with direct JavaScript SEO consequences.
Debugging JavaScript SEO with Search Console URL Inspection
No third-party crawler perfectly reproduces Googlebot’s renderer, which is why the ground truth for JavaScript SEO debugging is Google’s own URL Inspection tool in Search Console. It renders the page the way Googlebot does and shows you the result.
The workflow I run per template:
- Inspect the live URL. In Search Console, enter the URL, choose Test Live URL, then open View Tested Page.
- Read the rendered HTML. The HTML tab is what Googlebot’s renderer produced after executing your JavaScript. Confirm the H1, body copy, canonical, meta description, and JSON-LD are all present.
- Check the screenshot and page resources. The screenshot shows what rendered; More info → Page resources lists anything Googlebot couldn’t load — a blocked script here explains missing content.
- Diff against the browser DOM. Anything present in your browser’s rendered DOM but missing from the tested HTML is arriving too late to be reliably indexed.
Then cross-check for the AI layer, because URL Inspection only tells you what Google sees. Run the curl -A "GPTBot" fetch from earlier against the same URL. If content appears in URL Inspection’s rendered HTML but not in the raw curl response, you have a page that Google will index but that ChatGPT and Perplexity cannot read — the exact gap JavaScript SEO exists to close. Fold both checks into a recurring pass; they are Phase 3 of the 12-phase SEO and GEO audit.
From crawlable to citable: the JavaScript SEO payoff
Getting your content into the raw HTML makes it eligible — indexed by Google, readable by AI crawlers. Turning that eligibility into citations is a structural property of the page, and every move depends on the JavaScript SEO fundamentals holding.
Serve the answer, the schema, and the entity in raw HTML. An extractable answer, valid structured data, and a clear author entity are worthless to an AI engine if they arrive via JavaScript. All three have to survive the curl -A "GPTBot" test. Getting your JSON-LD into the server response rather than a useEffect is the single highest-leverage move, and the priority stack for it is in structured data for AI search.
Write self-contained, extractable passages. LLMs lift sentences that stand alone. Lead each section with a direct, complete answer and let the elaboration follow — the same inverted-pyramid shape that makes the FAQ blocks in this series independently quotable. The tactic-level version of this lives in how to get cited by ChatGPT.
That is why the arc is an arc and not a checklist. Citability sits downstream of crawlability: a perfect, quotable answer injected by JavaScript is invisible to the crawlers you wrote it for. Fix the render path, and the same work pays out twice — once in Google’s index, once in the AI answers built on top of it.
Where JavaScript SEO fits in the series
This guide is the umbrella. The framework spokes take the same crawlable-to-citable arc and ground it in one stack’s real APIs and gotchas — Astro SEO (islands and zero-JS-by-default), React SEO (escaping the SPA shell), and Next.js SEO (App Router server components and generateMetadata). Above it sits the SEO for engineers pillar, and alongside it the deeper treatment of render strategy in technical SEO for headless architecture.
If you would rather have the render path designed and shipped than diagnose it yourself, that is what I do: GEO and technical SEO consulting covers the crawl, schema, and citation-readiness layers, and full-stack development covers building the rendering strategy into the codebase. Either way, JavaScript SEO comes down to one discipline: get your content into the first raw-HTML wave, and everything downstream — ranking, rendering, and citation — has something to work with.
FAQ
Is JavaScript bad for SEO?
No. JavaScript is not inherently bad for SEO, but client-side rendering can be, because it delays your content until after a bot’s initial fetch. Google will render JavaScript in a second pass, but AI crawlers such as GPTBot and PerplexityBot generally will not. The safe pattern is to serve primary content in the server-rendered HTML and use JavaScript to enhance it, not to produce it.
Does Google index JavaScript-generated content?
Yes. Google renders JavaScript with an evergreen version of Chromium and indexes the resulting content, and it now renders effectively all indexable HTML pages at a median of about 10 seconds after crawling. The caveat is that rendering is a deferred second wave, so content that only exists after hydration can be indexed late or partially. Anything critical belongs in the first raw-HTML response.
Do AI crawlers like GPTBot execute JavaScript?
Generally no. GPTBot, OAI-SearchBot, ClaudeBot, and PerplexityBot fetch raw HTML and parse it as text rather than running a headless browser the way Googlebot does. A page whose content, schema, or author signals are injected client-side is effectively blank to them, even when it ranks perfectly well in Google. Confirm it yourself with curl -A "GPTBot" <url> and check that your H1, body copy, and JSON-LD are in the response.
What’s the difference between client-side and server-side rendering for SEO?
With server-side rendering, the server runs the JavaScript and returns complete HTML, so the content is in the first wave that every crawler reads. With client-side rendering, the server returns a near-empty shell and the browser assembles the page, so Googlebot has to render it in a later pass and AI crawlers see nothing. For any content that needs to rank or be cited, server-side rendering, static generation, or incremental regeneration is the safer default.
How do I check whether my JavaScript content is crawlable?
Run two checks. First, curl -A "GPTBot" <url> and confirm your H1, body text, and JSON-LD are in the raw response — that is what AI crawlers see. Second, use Search Console’s URL Inspection tool, choose Test Live URL, and read the rendered HTML and page resources to confirm what Googlebot produces. Anything present in the browser but missing from both is arriving too late to be reliable.
Is dynamic rendering still recommended for JavaScript SEO?
No. Google now describes dynamic rendering as a workaround rather than a long-term solution and recommends server-side rendering, static rendering, or hydration instead. It adds infrastructure and a second code path to maintain, and modern meta-frameworks make proper server rendering straightforward enough that the workaround is rarely justified.