Google can render JavaScript. That does not mean it renders yours reliably, quickly, or the way you expect. Most JavaScript SEO problems come down to one gap: what a user sees after the page fully loads is different from what Googlebot sees when it processes the page. Close that gap and most issues disappear.
This guide explains how Google handles JavaScript, the problems we see most often on React, Vue and Angular sites, and how to diagnose and fix each one.
How Google processes JavaScript pages
Googlebot works in three stages:
- Crawl. It fetches the raw HTML returned by your server.
- Render. It queues the page for the Web Rendering Service, which runs an evergreen version of Chromium, executes your JavaScript, and produces the final DOM.
- Index. It extracts content, links and metadata from the rendered result.
Rendering usually happens quickly, but it is a separate step with its own constraints. If content, links or metadata exist only after JavaScript runs, you are relying on that step to succeed every time. Other crawlers are less capable. Many AI assistants' crawlers and several social preview bots do not execute JavaScript at all, so a client-rendered page can be invisible to them.
The most common JavaScript rendering issues
1. Content only exists after client-side rendering
A single-page app often ships an almost empty HTML shell, something like <div id="root"></div>, and builds everything in the browser. Google can usually render it, but any failure (a blocked script, an API timeout, a runtime error) leaves Google with an empty page. That frequently shows up as "Soft 404" or "Crawled – currently not indexed" in Search Console.
Fix: Use server-side rendering (SSR) or static generation (SSG) for any page you want indexed. Next.js, Nuxt, SvelteKit, Remix and Astro all support this out of the box.
2. Links that are not real links
Google discovers pages by following <a href="..."> elements. It does not click buttons or trigger onclick handlers. Navigation built from <div onclick="navigate()"> or <span> elements is invisible to crawlers.
Fix: Every navigational element should be an anchor with a real, crawlable href. Framework link components (next/link, router-link) render proper anchors, so use them rather than custom click handlers.
3. Metadata set only by JavaScript
Titles, meta descriptions, canonical tags and robots directives injected on the client are risky. A common and damaging pattern is a page that ships noindex in the initial HTML and removes it with JavaScript. Google may see the noindex first and skip rendering entirely.
Fix: Render all head metadata on the server. Never rely on JavaScript to remove a noindex.
4. Error pages that return 200
In a client-rendered app, a missing product or deleted article often shows a friendly "Not found" message while the server still returns HTTP 200. Google classifies these as soft 404s, and they waste crawl budget.
Fix: Return a real 404 status from the server for missing content. If you cannot, add <meta name="robots" content="noindex"> on error views as a fallback.
5. Blocked resources
If robots.txt blocks your JavaScript bundles, CSS or the API endpoints that supply content, Google renders an incomplete page.
Fix: Allow crawling of the scripts, styles and APIs needed to render public pages. Blocking /api/ is fine only if pages are server-rendered and do not fetch content from it at render time.
6. Content behind user interaction
Googlebot does not scroll, click tabs or press "Load more". Content that appears only after interaction, including infinite scroll with no paginated fallback, may never be indexed.
Fix: Put important content in the initial render. For long lists, provide paginated URLs (for example ?page=2) linked with normal anchors.
7. Hash-based routing
URLs like example.com/#/pricing are treated as a single page, because Google ignores everything after the #.
Fix: Use the History API so each view has a clean path such as /pricing.
8. Slow or heavy JavaScript
Large bundles delay rendering for users and hurt Core Web Vitals, especially Interaction to Next Paint (INP). Heavy hydration on mobile devices is a frequent cause of poor field data.
Fix: Split code by route, defer non-critical scripts, remove unused dependencies, and consider partial hydration or server components where your framework supports them.
How to see what Google actually sees
- URL Inspection in Search Console. Run a live test, then view the rendered HTML and screenshot. If your main content, links or canonical tag are missing there, Google does not see them.
- View source vs inspect element. "View source" shows the raw HTML the server sent. "Inspect" shows the rendered DOM. Anything that exists only in Inspect depends on JavaScript.
- Disable JavaScript in your browser. A crude but fast test. If the page is blank, crawlers that do not render will see the same.
- Crawl with rendering on and off. Screaming Frog and Sitebulb can crawl both ways. Compare word counts, link counts and titles between the two runs to spot pages that depend on JavaScript.
- Check the Pages report. Spikes in "Soft 404" or "Crawled – currently not indexed" on JS-driven templates are often a rendering symptom.
Choosing a rendering strategy
| Strategy | How it works | Best for |
|---|---|---|
| Static generation (SSG) | HTML built at deploy time | Blogs, docs, marketing pages |
| Server-side rendering (SSR) | HTML built per request | Frequently changing or personalised pages |
| Incremental regeneration | Static pages rebuilt on a schedule or on demand | Large catalogues and news |
| Client-side rendering (CSR) | HTML built in the browser | Logged-in dashboards and tools that do not need indexing |
Dynamic rendering (serving pre-rendered HTML only to bots) is now described by Google as a workaround rather than a recommended solution. If you are starting fresh, choose SSR or SSG instead.
A JavaScript SEO checklist
- Primary content is present in the server-rendered HTML
- Title, meta description, canonical and robots tags are rendered on the server
- All navigation uses
<a href>links with clean URLs - Missing pages return a real 404 status
- robots.txt does not block required JS, CSS or content APIs
- Structured data appears in the rendered output
- Paginated URLs exist as a fallback for infinite scroll
- Core Web Vitals pass in field data for key templates
Running a headless or JavaScript-heavy site? Our headless CMS and SEO services teams build rendering setups that search engines and AI crawlers can read in full. For related fixes, see our guide to internal linking best practices.
