JavaScript SEO: Making JavaScript-Heavy Sites Search-Engine Friendly

8 sections 5 min read

JavaScript SEO is the work of making sure content and links that depend on JavaScript can still be crawled, rendered, and indexed. A JavaScript-heavy site can look complete to every visitor and still show search engines an empty shell, a page missing its main text, or links a crawler cannot follow. Making it search-engine friendly comes down to three decisions: where the HTML is produced, how links and navigation are built, and how you check what crawlers receive.

What Google Does With a JavaScript Page

Google’s JavaScript SEO basics describe a pipeline of crawling, rendering, and indexing. Googlebot first fetches the HTML, then queues every page that returns a 200 status for rendering, unless a robots meta tag or header rules out indexing. A page can wait in that queue for a few seconds or longer. When resources allow, a headless Chromium browser renders the page and runs its JavaScript, and Google indexes the rendered result.

Two consequences matter for a JavaScript site. Links that are already in the HTML response can be found when Google first parses the page, and content in that response does not depend on scripts running. Content that only appears after scripts run depends on rendering succeeding: a script error, a blocked file, or an interaction the renderer never performs can leave it out.

On timing, the public figures are dated. At Chrome Dev Summit 2019, Google’s Martin Splitt said the median time for Googlebot to render was five seconds, as Search Engine Roundtable reported. A median says nothing about the slow end. Onely’s own crawl experiment found that “Google needed 9x more time to crawl JavaScript pages vs plain HTML pages.” Neither figure tells you how a specific page on your site is treated; Search Console does.

Other Crawlers Are Not Google

Bing’s 2018 post on bingbot and JavaScript recommended dynamic rendering for sites that rely heavily on JavaScript, a position that dates from that year and differs from Google’s current guidance below.

AI crawlers are a sharper case. In a December 2024 analysis of crawler traffic, Vercel reported that “none of the major AI crawlers currently render JavaScript,” and that ChatGPT and Claude crawlers fetch JavaScript files without executing them. Content that exists only after client-side rendering can be invisible to those crawlers even when Google has indexed it.

Choosing Where the HTML Comes From

Google still renders pages whose content arrives in the HTML; its documentation notes that pages with a 200 status go to the rendering queue “no matter whether JavaScript is present on the page.” What changes is that the main content no longer waits for client-side JavaScript to run, which is why Google’s documentation points to server-side rendering, static rendering, or hydration. The frameworks that do this are layers over common JavaScript libraries:

Library Framework layer that adds server or build-time rendering
React Next.js
Vue Nuxt
Svelte SvelteKit

Server-side rendering builds the HTML for each request; static generation builds it once at deploy time; hydration sends that HTML and then attaches the JavaScript that makes it interactive. Pages that need to rank benefit most. Logged-in dashboards and tools with no search value can stay client-side.

Dynamic Rendering Is a Workaround

Dynamic rendering sends crawlers a pre-rendered HTML version while users get the client-side application. Google’s current documentation calls dynamic rendering a workaround it does not recommend, because of the extra complexity and resources it requires, and recommends server-side rendering, static rendering, or hydration instead. It also states that “Googlebot generally doesn’t consider dynamic rendering as cloaking,” while “Using dynamic rendering to serve completely different content to users and crawlers can be considered cloaking.” Treat an existing dynamic rendering setup as something to migrate away from, and keep both versions identical in content while it runs.

Google’s link guidance asks for links built as <a> elements with an href attribute. A click handler on another element does not give the crawler a URL to follow:

<!-- A link Google can follow -->
<a href="/products/trail-shoes">Trail shoes</a>

<!-- No URL for a crawler to follow -->
<span onclick="goTo('/products/trail-shoes')">Trail shoes</span>

Three patterns on JavaScript sites hide content in the same way:

  • “Load more” buttons and infinite scroll with no paginated URLs behind them.
  • Hash-based routes (/#/products) for content that should be its own page; History API routes (/products) give each view a real URL.
  • Content that appears only after a click, tap, or hover, which Google’s mobile-first indexing guidance specifically warns against for primary content.

Keeping Rendering From Failing

  • Put the main content and key links in the initial HTML wherever you can, even inside a mostly client-side app.
  • Do not block the JavaScript and CSS files a page needs in robots.txt; the renderer has to fetch them.
  • Catch errors so one failed script does not stop the rest of the page from rendering.
  • Keep scripts lean and defer what is not needed for the first view, which also helps Core Web Vitals.
  • Set the canonical, title, and robots meta tag in the server’s HTML, not only after scripts run. A noindex in the initial HTML cannot be undone by a script: Google states that when it encounters the noindex tag, “it may skip rendering and JavaScript execution.”
  • Give unknown routes in a single-page app a real error signal. Google’s guidance is to redirect them with JavaScript to a URL where the server responds with a 404 status, or to add a noindex robots meta tag to the error view.

Checking What Google Receives

  1. Compare “View Source” with the rendered DOM in your browser’s developer tools. Content in the second and not the first depends on rendering.
  2. Run the page through the URL Inspection tool and its live test, and look at the rendered HTML and any resources that failed to load.
  3. Watch the browser console and production error monitoring for script errors.
  4. Use the Page indexing report to spot templates whose pages are crawled but not indexed, then inspect one page from that template.

Questions About JavaScript SEO

Does every page need server-side rendering?
No. Pages that need to be found in search benefit most. Interactive tools and logged-in areas with no search value can render in the browser.

Does server-side rendering make Google crawl a page sooner?
No. It puts the main content in the first response, so indexing it does not depend on scripts running correctly. How often Google crawls the page is a separate matter.