JavaScript SEO is the part of technical SEO that controls how pages built or changed by JavaScript are crawled, rendered, and indexed, and what each dependency on rendering costs the engine that reads them. This article covers what JavaScript SEO is, the crawl, render, and index sequence Google follows, client-side versus server-side rendering, links and content that depend on JavaScript, and how to test what the crawler sees. It then shows JavaScript SEO on holisticradar.com, connects JavaScript SEO to engineering for cost of retrieval, and closes with frameworks, hydration, and AI crawlers that do not execute JavaScript.
What JavaScript SEO is
JavaScript SEO is the discipline that decides which facts and links on a page depend on JavaScript executing before a search engine can read them. Almost every modern site runs JavaScript somewhere, so the useful question is never whether a site uses it. The useful question is which content waits for it.
A fact that exists in the HTML response costs the engine one request to read. A fact that exists only after scripts run costs that request, a place in a rendering queue, a headless browser session, and every script, stylesheet, and API call the page makes along the way. Each added dependency is a cost the engine has to pay before it can index the fact, and an engine that declines to pay it never sees the fact at all.
JavaScript SEO therefore governs three decisions on every template:
- Whether indexable content is present in the initial HTML or only in the rendered HTML.
- Whether links are real anchor elements that a crawler can follow without running code.
- Whether status codes, canonical tags, and robots meta tags are true in the first response, before any script changes them.
A page that answers all three correctly costs almost nothing extra to render. A page that gets any of them wrong pays the cost on every crawl, and the cost grows with the number of URLs that share the template.
The crawl, render, and index sequence
Google processes JavaScript pages in three phases, crawling, rendering, and indexing, and rendering can be deferred until resources allow. Google Search Central describes the sequence in its JavaScript SEO basics documentation:
- Crawl. Googlebot takes a URL from the crawl queue, checks robots.txt, and requests the page if crawling is allowed. It parses the HTML response and adds URLs found in the href attributes of links to the crawl queue.
- Queue for rendering. Pages that return a 200 status are placed in a render queue. Google states that a page may wait there for a few seconds, and that it can take longer.
- Render. A headless, evergreen version of Chromium executes the page’s JavaScript and builds the rendered DOM.
- Index. Google parses the rendered HTML for links again, queues any new URLs, and uses the rendered HTML to index the page.
Four consequences follow from that sequence, and each one is a cost that falls on JavaScript dependent content.
First, deferral: content that appears only after rendering is indexed after the render step completes, which is later than content in the initial response. A new page whose body text is injected by a script waits longer to be indexed than the same page served as HTML.
Second, blocked resources: Google does not render JavaScript from files or pages that robots.txt blocks. A disallowed script directory can leave a page rendered without its content.
Third, robots signals: when Google finds a noindex directive in the initial HTML, it may skip rendering. A script that removes the noindex later is never executed, so the page stays out of the index.
Fourth, status codes: pages that return a non-200 status may skip rendering, which is why single-page applications that return 200 for every route create soft 404s instead of real ones.
Client-side vs server-side rendering
Client-side rendering builds the page in the visitor’s browser after scripts run, while server-side rendering sends finished HTML in the first response. The choice decides where the rendering cost lands: on the server once, or on every browser and every crawler on every visit.
| Rendering pattern | Where the HTML is built | What the crawler receives first | Rendering cost to the engine |
|---|---|---|---|
| Client-side rendering (CSR) | In the browser, after the JavaScript bundle loads and runs | A near-empty shell with script tags | Highest: every fact and link waits for the render step |
| Server-side rendering (SSR) | On the server, per request | Complete HTML with content and links | Low: rendering confirms what the HTML already states |
| Static generation (prerendering) | At build time, served from cache | Complete HTML, usually fastest to arrive | Lowest: no per-request work on either side |
| Hydration | On the server first, then scripts attach behavior in the browser | Complete HTML, followed by the scripts that make it interactive | Low for indexing; the cost moves to download size and main-thread work |
Google’s documentation names server-side rendering, static rendering, and hydration as the long-term solutions, and describes dynamic rendering, which serves crawlers a prerendered copy while users get the client-side version, as a workaround rather than a recommended pattern. The reason is maintenance cost: two versions of every page have to stay identical, and a difference between them is a source of indexing errors that nobody sees in the browser.
Client-side rendering is not unindexable. Google can render it, and many client-side sites are indexed. The cost is delay, dependence on every resource loading inside the render budget, and invisibility to any crawler that does not execute JavaScript.
Links and content that depend on JavaScript
Googlebot discovers links from anchor elements with an href attribute, so navigation that exists only as a click handler is not a link to the crawler. An a element whose href holds a resolvable URL is followed. A span, a div, or a button that changes the page through an onclick handler is not followed, and neither is an anchor with no href or with a JavaScript pseudo-URL in place of a real one.
Routing has the same rule. Google’s documentation recommends the History API for client-side routing and states that Googlebot cannot reliably resolve URLs that differ only by a fragment. A site that changes views through the part of the URL after the hash presents one URL to the crawler, whatever the visitor sees.
Content follows a related rule: Googlebot does not click, type, or swipe. Content that loads only after a tap on a tab, a press of a “load more” button, or a search input is not loaded during rendering. Content that is already in the DOM and hidden with CSS, such as the closed panels of an accordion, is present in the rendered HTML and can be indexed.
Three further dependencies cause most JavaScript SEO failures in audits:
- Titles, meta descriptions, and canonical tags written by a script, which may differ between the initial and the rendered HTML and send the engine two conflicting signals.
- Body content fetched from an API after load, which fails silently when the API is slow, rate-limited, or blocked by robots.txt.
- Structured data injected by a tag manager, which arrives late and is easy to duplicate or contradict.
Each of these removes a fact from the cheapest place an engine can read it and moves it to the most expensive one.
Testing what the crawler sees
The URL Inspection tool in Google Search Console shows the rendered HTML Googlebot produced for a URL, which makes it the most direct test of what Google sees. A complete test compares that rendered HTML with the initial response and with a browser that runs no scripts.
| Test | What it shows | What a failure means |
|---|---|---|
| View source of the initial HTML | The response before any script runs | A key fact or link missing here depends on rendering |
| URL Inspection live test, rendered HTML | The DOM after Googlebot’s Chromium executed the page | A fact missing here is not indexed at all |
| URL Inspection, page resources and console messages | Resources that failed to load and JavaScript errors during rendering | A blocked or failing resource is removing content |
| Rich Results Test, rendered code | Rendered HTML and detected structured data, without Search Console access | Structured data is missing or conflicts after rendering |
| Browser with JavaScript disabled | What a non-rendering crawler receives | The page is invisible to crawlers that do not execute scripts |
| Exact-phrase search for a sentence from the page | Whether the indexed copy contains that sentence | Rendered content was not indexed, or not yet |
The test is run per template, not per URL. One article, one service page, one category, and one product page usually cover a site, because pages that share a template share their rendering behavior. The result of each test is a pass or a fail for one fact: is the answer to the page’s main question present, and is every link to the page’s children and siblings present, at the stage the test inspects.
JavaScript SEO on holisticradar.com
Holistic Radar serves all content and every internal link on holisticradar.com as server-rendered HTML from PHP templates, so nothing the crawler needs waits for the render step. The site runs on WordPress with a custom theme, and the templates print the page before any script is involved.
The division of work is strict. HTML carries every fact: the H1, the extractive summary at the top of each article, every heading and the triple sentence under it, every table, the single bridge link, the next-read links, the breadcrumb trail, and the navigation. The theme outputs the one JSON-LD schema graph (Organization, WebSite, founder Person, WebPage, Service, BlogPosting, FAQPage, and BreadcrumbList) directly in the page head, so structured data is never injected by a tag manager or a client-side script.
JavaScript is limited to deferred scripts that run motion and the journey interaction. Because those scripts are deferred and carry no content, they neither block the first paint nor change anything an indexer reads. The measurable result is that the mobile PageSpeed test, run in September 2026 with Lighthouse 13.5 on slow 4G, records a Total Blocking Time of 0 ms and a performance score of 91.
The practical check is simple. Viewing the source of any article on the site shows the summary, the bridge link, and the schema graph in the response itself. A crawler that executes no JavaScript at all receives the same facts and the same link graph as Googlebot after rendering, which is the condition this article recommends for every template.
JavaScript SEO and engineering for cost of retrieval
JavaScript SEO is the rendering part of engineering for cost of retrieval: it measures what a page costs an engine to render, and engineering removes that cost in the template. Rendering is the second of the six stages of cost of retrieval, after crawl and before parse, extract, verify, and serve, and a fact that fails at that stage never reaches the later ones.
Fixing rendering page by page does not hold. The decision that puts content in the initial HTML is made in the template, the framework, and the caching layer, and it has to be made again every time a component, a plugin, or a third-party script is added. That is engineering work on the technical layer, the first layer in the repair order.
A reader who has diagnosed a rendering dependency needs the build decisions that remove it for every template at once, and keep it removed as the site grows.
→ Engineering for cost of retrieval: how Holistic Radar builds templates that keep every indexable fact in the initial HTML.
JavaScript SEO for frameworks, hydration, and AI crawlers
Does a React or Vue site need server-side rendering for SEO?
A React or Vue site needs its indexable content and links in the initial HTML, and server-side rendering or static generation is the usual way to provide them. Frameworks built on these libraries, such as Next.js and Nuxt, render on the server or at build time by default for that reason. A purely client-side build can be indexed by Google, but it waits for the render step and is invisible to crawlers that do not execute JavaScript.
Does hydration hurt SEO?
Hydration does not hurt indexing when the server HTML is complete, because the crawler already has every fact before the scripts attach behavior. The cost of hydration moves to responsiveness: a large bundle that hydrates the whole page occupies the main thread and raises Total Blocking Time and Interaction to Next Paint. Partial hydration, which makes only the interactive components load scripts, keeps that cost proportional to the interactivity the page actually has.
Do AI crawlers execute JavaScript?
Most AI crawlers do not execute JavaScript, so content that exists only after rendering is invisible to them. A 2024 analysis of crawler traffic published by Vercel found that crawlers from OpenAI, Anthropic, Perplexity, and Meta fetched HTML without rendering it, while Gemini relied on Google’s own rendering infrastructure. A page that depends on client-side rendering can therefore be indexed by Google and still be absent from answers built on other indexes.
Is dynamic rendering still acceptable?
Dynamic rendering still works, but Google describes it as a workaround and recommends server-side rendering, static rendering, or hydration instead. Serving crawlers a different version from the one users receive doubles the surface that has to be tested, and any drift between the two versions produces indexing problems that do not appear in a browser.