JavaScript SEO is the work of making sure search engines can find, render and index content on sites built with JavaScript frameworks such as React, Vue, Angular and Next.js. Google renders JavaScript, so a JS site can rank. The catches are timing, crawlable links and the other crawlers that do not run scripts at all.
How does Google process JavaScript pages?
Google’s documentation describes three stages: crawling, rendering and indexing. Googlebot fetches the URL and reads the raw HTML. Every page that returns a 200 status then enters a render queue. A headless Chromium runs the scripts when Google has the resources, and the rendered HTML is what gets indexed. A page can wait in that queue for seconds or longer.
Three consequences follow:
- Content that exists only after JavaScript runs reaches the index later than content in the raw HTML.
- Blocked files stay unrendered. Google’s documentation says it will not render JavaScript from files or pages blocked in robots.txt (see robots.txt and noindex).
- Not every bot renders. Google says server-side rendering or pre-rendering is still worthwhile, because not all bots run JavaScript. Put important text and links in the HTML response so every crawler gets them.
Why does JavaScript SEO matter?
A client-side rendered page can look perfect in your browser and arrive nearly empty in the raw HTML: a <div id="root"></div> and a script tag. If rendering fails or lags, Google sees little. For sites whose product, article or category content loads through JavaScript, this decides whether pages get indexed. That is why the impact is high for JS-heavy sites and zero for plain HTML sites.
What does Google need from your JavaScript site?
| Area | What Google says |
|---|---|
| Links | Google finds links only in <a> elements with an href. Buttons, onclick handlers and <span> links are not followed. |
| URLs | Use the History API (pushState) with real paths. Fragment URLs such as /#/products are not reliably handled. |
| Rendering | Server-side rendering, static rendering or hydration are the recommended routes. Bot-only pre-rendering (serving a pre-rendered copy only to crawlers) is described as a workaround and not a recommended solution. |
| Canonical | Best set in the HTML source. If JavaScript sets it, it must match the HTML value, with only one canonical tag on the page. |
noindex |
Do not ship noindex in the original HTML and plan to remove it with JavaScript: a noindex can stop rendering, so the removal may never run. |
| Status codes | Use meaningful status codes. In a single-page app, redirect missing routes to a URL that returns a real 404, or add noindex on error views, to avoid soft 404s. |
| Titles and descriptions | JavaScript can set them. Make each page’s unique. |
| Structured data | JSON-LD generated by JavaScript works, but test it. |
| Caching | Use content fingerprints in file names, because Google’s renderer may ignore caching headers. |
How do you fix JavaScript SEO, step by step?
- See what Google sees. In Search Console, run URL Inspection on a page, then View tested page and read the rendered HTML and screenshot. Is your main text, heading and navigation there?
- Compare source and render. In your browser, open View Source (raw HTML) and the DevTools Elements panel (rendered DOM). The gap between them is the content that depends on scripts.
- Render important pages on the server. For landing pages, articles, products and categories, use server-side rendering or static generation. Frameworks such as Next.js, Nuxt, Astro and SvelteKit offer both. Reserve client-side rendering for logged-in app screens.
- Use real links. Replace click handlers with
<a href="/pricing/">. Make sure navigation, pagination and related links exist as anchors in the HTML (see internal linking). - Give every view its own URL through the History API, and avoid hash routing.
- Return honest status codes. Missing routes must produce a real 404 (see custom 404 page). Moved pages need server-side redirects.
- Leave scripts crawlable. Do not disallow your JS and CSS files in robots.txt.
- Keep it fast. Heavy bundles hurt Core Web Vitals, especially INP and LCP. Split code, defer what is not needed above the fold, and lazy-load images below it.
- Retest after each release, because a framework upgrade or a new component can break rendering silently.
How do you measure the result?
- URL Inspection on a sample of templates: indexed, rendered text present, correct canonical.
- The Page indexing report: watch “Crawled, currently not indexed” and “Discovered, currently not indexed” counts after you change rendering (see crawl and indexing errors).
- A crawler with JavaScript rendering on and off. Pages with big differences in word count or link count between the two modes are at risk.
- A search for a distinctive sentence from a JS-loaded section in quotes: if the page appears, the content is indexed.
What mistakes should you avoid?
- Links without
hrefor built with click events only. - Hash-based routing for pages you want indexed.
- Soft 404s: an app that shows “Page not found” while returning 200.
- Blocking JS or CSS in robots.txt.
- Relying on bot-only pre-rendering as a permanent fix. Google says it adds extra work and resource needs. Google does not treat it as cloaking when the content is similar for bots and users, but showing bots very different content counts as cloaking.
- Content behind interactions: text that loads only after a click or scroll event is not rendered.
- Testing only in your own browser, which has a warm cache and a fast machine.
Where can you learn more?
The free digital marketing module of the Fe/male Switch startup school covers the SEO foundations that a framework choice should respect. Next steps on SEO100: check indexing in the page indexing report and read the canonical tags rules for JavaScript sites.