HomeAboutServices PortfolioSkillsToolsBlog TestimonialsContact

JavaScript SEO: how to make JS websites crawlable and indexable

Key takeaways

  • Google can render JavaScript, but it does so in a separate queue after the initial HTML crawl, creating delays and potential failures that can prevent content from being indexed.
  • The safest approach for SEO is to send important content in the initial HTML response using server-side rendering (SSR) or static site generation (SSG), rather than relying on client-side JavaScript.
  • Common JavaScript SEO problems include empty initial HTML, JavaScript-dependent links, missing meta tags, and soft 404 errors that look like real pages to Googlebot.
  • Frameworks like Next.js, Nuxt, and Astro solve most JavaScript SEO problems by providing SSR and SSG out of the box, while keeping the interactive experience JavaScript enables.
  • Use Google Search Console's URL Inspection tool to compare raw HTML with rendered output and catch JavaScript rendering issues before they affect rankings.
Computer screen showing JavaScript code, representing the intersection of web development and SEO

JavaScript can make your website invisible to Google. A page can look perfect in a browser, load fast for users, and deliver a great experience, while simultaneously appearing as a blank page to search engines. This happens because Google processes JavaScript differently from how your browser does, and the gap between those two processes is where rankings are won or lost.

I work with both SEO and front-end development, building sites with React and Next.js while also optimizing them for search. That dual perspective is why I know this problem matters more than most developers realize. A beautifully coded React app that Google cannot index is invisible to 90% of the internet's discovery layer.

This guide explains exactly how Google handles JavaScript, why client-side rendering creates SEO problems, which rendering strategies solve those problems, and how to diagnose and fix JavaScript SEO issues on your own site.

How Google handles JavaScript

Google processes web pages in two distinct phases, and understanding this pipeline is the foundation of JavaScript SEO.

Phase 1: Crawl

Googlebot makes an HTTP request to your URL and receives the initial HTML response from your server. At this stage, Google sees exactly what your server sends, nothing more. If your server sends a complete HTML document with all your content, headings, meta tags, and links, Google has everything it needs right here. If your server sends an empty HTML shell with a single <div id="root"></div> and a JavaScript bundle, Google sees an empty page.

Phase 2: Render

Google places the page in a rendering queue and eventually processes it using a headless Chromium browser (the same engine that powers Chrome). This step executes your JavaScript, builds the DOM, and attempts to produce the final rendered page. The rendered content is then used for indexing.

The gap between crawl and render

Here is the critical problem: these two phases do not happen simultaneously. There is a queue between them, and that queue can introduce delays ranging from seconds to days. During that gap:

  • Content that only exists after JavaScript execution is invisible to Google's initial crawl
  • Links that are only generated by JavaScript may not be discovered during the crawl phase, slowing the discovery of linked pages
  • Meta tags injected by JavaScript (like React Helmet) are not present in the initial HTML response
  • If the render fails for any reason (timeout, error, third-party script blocking), the content may never be indexed

The rendering problem: why JavaScript breaks SEO

The rendering problem is simple to state: if important content exists only in JavaScript and not in the initial HTML, you are betting your rankings on Google's rendering queue working perfectly every time. That is a bet you should not make.

Why rendering fails

  • Timeouts: Google allocates limited time and resources to render each page. Complex JavaScript that takes too long to execute may be cut off before completion.
  • Third-party scripts: If your page depends on an external API or CDN that is slow or temporarily down during Google's render attempt, the content will be incomplete.
  • User interaction requirements: Content that loads only after a click, scroll, or other user interaction will never be rendered by Googlebot. Googlebot does not click buttons or scroll pages.
  • JavaScript errors: A single uncaught error can prevent the rest of your JavaScript from executing, leaving the page partially rendered.
  • Resource blocking: If your robots.txt blocks CSS or JavaScript files that are needed for rendering, Google cannot execute them.

Rendering strategies compared

How you render your pages determines whether Google sees your content in phase 1 (crawl) or has to wait for phase 2 (render). Here are the four main approaches:

Client-side rendering (CSR)

The server sends a minimal HTML shell. JavaScript runs in the browser and builds the entire page. This is the default behavior of React (Create React App), Vue CLI, and Angular CLI.

SEO impact: the worst option for content that needs to rank. Google sees an empty page during the crawl phase and must wait for rendering. If rendering fails, the content is lost.

Server-side rendering (SSR)

The server executes JavaScript on each request, generates the complete HTML, and sends it to the browser. The page is fully visible to Google during the crawl phase. JavaScript then "hydrates" the page in the browser to add interactivity.

SEO impact: excellent. Google receives complete HTML immediately. This is the recommended approach for content-heavy pages that need to rank. Next.js, Nuxt.js, and Angular Universal support this natively.

Static site generation (SSG)

Pages are pre-rendered at build time and served as static HTML files. The content is fixed until the next build, making this ideal for content that does not change frequently (blog posts, documentation, landing pages).

SEO impact: the best option for static content. Pages load the fastest, Google sees everything immediately, and there is zero rendering risk. This is how my own site works: hand-coded HTML served from Cloudflare Workers, with no JavaScript required for content rendering.

Incremental static regeneration (ISR)

A hybrid of SSG and SSR. Pages are pre-rendered at build time but can be regenerated on demand when the data changes. Next.js introduced this pattern.

SEO impact: excellent. Combines the speed and reliability of static HTML with the freshness of server-rendered content.

StrategyInitial HTMLSEO riskBest for
CSREmpty shellHighLogged-in dashboards, tools
SSRFull contentLowDynamic pages (e-commerce, news)
SSGFull contentNoneBlogs, docs, landing pages
ISRFull contentLowFrequently updated content
JavaScript code on a dark screen, the type of client-side rendering that needs special SEO handling

Common JavaScript SEO problems and fixes

Empty initial HTML

Problem: your server returns an HTML file that contains only a <div id="root"> and a script tag. All content, including titles and headings, is generated by JavaScript in the browser.

Fix: switch to SSR or SSG. If migrating the entire application is not practical, at minimum ensure that the title tag, meta description, canonical tag, and heading structure are present in the server-rendered HTML. Frameworks like Next.js make this straightforward with their built-in metadata APIs.

JavaScript-dependent links

Problem: navigation links use JavaScript event handlers (onClick) instead of standard <a href> tags. Googlebot discovers new pages by following links in the HTML. If your links are not real anchor tags, those pages may never be discovered.

Fix: always use standard <a href="/path"> tags for navigation. You can still attach JavaScript event handlers for enhanced behavior, but the underlying HTML must be a real link. For more on link structure, see my internal linking strategy guide.

Hash-based routing

Problem: your SPA uses hash routing (e.g., example.com/#/about). Google treats everything after the hash as a fragment identifier, not a separate URL. All your "pages" resolve to the same URL in Google's eyes.

Fix: use the History API for routing (pushState-based routing). All modern frameworks support this: React Router, Vue Router, and Angular Router all use it by default. Your URLs should be clean paths like /about/, not hash fragments.

Soft 404 errors

Problem: when a page does not exist, your server returns a 200 status code (because it always returns the same HTML shell), and JavaScript displays a "Page not found" message in the browser. Google indexes the empty 200 response as a real page.

Fix: configure your server to return proper HTTP status codes. Non-existent URLs should return 404. Redirected URLs should return 301 or 302. Your server must handle these at the HTTP level, not in the JavaScript layer.

Lazy-loaded content below the fold

Problem: content that loads only when the user scrolls to it (infinite scroll, lazy-loaded sections) may never be seen by Googlebot, which does not scroll.

Fix: make all indexable content available in the initial render. Lazy loading is fine for images and non-critical visual elements, but text content that needs to rank should be in the HTML from the start. Use proper image lazy loading with the loading="lazy" attribute rather than scroll-triggered JavaScript.

JavaScript frameworks and SEO

Not all JavaScript frameworks create the same SEO challenges. Here is how the most popular ones compare:

Next.js (React)

The gold standard for React SEO. Supports SSR, SSG, and ISR out of the box. The App Router provides built-in metadata handling, automatic code splitting, and streaming server components. If you are building a React application that needs to rank, Next.js is the right choice.

Nuxt (Vue)

The Vue equivalent of Next.js. Supports SSR and SSG with a similar developer experience. Nuxt 3 provides excellent SEO defaults including automatic head management and server-side rendering.

Astro

A static-first framework that ships zero JavaScript to the browser by default. You can add interactive "islands" of React, Vue, or Svelte where needed. For content-heavy sites, Astro produces the lightest, fastest pages with the best SEO characteristics of any modern framework.

Gatsby (React)

A static site generator for React. Pre-renders pages at build time and produces fast, SEO-friendly output. Best for sites with content that does not change frequently.

Angular Universal

Adds SSR to Angular applications. More complex to set up than Next.js or Nuxt, but effective for Angular-based projects that need server-rendered HTML.

Plain React, Vue, or Angular (CSR only)

Without SSR or SSG, these frameworks produce client-side rendered applications that are the hardest to optimize for search. Avoid for any content that needs to rank.

How your JavaScript application handles links and routing directly impacts whether Google can discover and crawl your pages.

Rules for SEO-safe navigation

  • Use real anchor tags: every navigational link must be an <a href="/path"> element. Googlebot follows href attributes to discover pages. <button onClick> or <div onClick> elements are invisible to the crawler.
  • Use clean URL paths: /products/blue-widget/ is crawlable and keyword-rich. /#/products?id=4829 is not.
  • Generate sitemaps: for JavaScript applications with many pages, an XML sitemap is essential. It gives Google a complete list of URLs to crawl, bypassing any link-discovery issues. Submit it through Google Search Console.
  • Include breadcrumbs: breadcrumb navigation provides additional internal links and helps Google understand your site hierarchy. Add BreadcrumbList schema for extra visibility.

Meta tags and structured data

Meta tags and structured data are among the most commonly broken elements in JavaScript applications.

The problem with client-side meta tags

In a default React SPA, every page shares the same <title> and <meta description> from the index.html file. Libraries like React Helmet update these tags in the browser after JavaScript executes, but Google may see the original generic tags during the crawl phase.

The fix

Render all meta tags server-side. With Next.js, use the Metadata API in your page components. With Nuxt, use the useHead composable. The critical tags that must be in the initial HTML response:

  • <title> tag with page-specific title
  • <meta name="description"> with page-specific description
  • <link rel="canonical"> with the correct canonical URL
  • Open Graph and Twitter Card meta tags for social sharing
  • JSON-LD structured data for Article, Product, FAQ, or other relevant types

My technical SEO guide covers all the essential meta tags and their correct implementation.

JavaScript and Core Web Vitals

Heavy JavaScript is the primary cause of poor Core Web Vitals scores. Here is how JavaScript impacts each metric:

Largest Contentful Paint (LCP)

If your largest content element (usually a hero image or heading) is rendered by JavaScript rather than present in the initial HTML, the browser has to download, parse, and execute JavaScript before it can even start rendering that element. This pushes LCP far beyond the 2.5-second threshold Google recommends.

Fix: make sure LCP elements are in the server-rendered HTML. Use SSR or SSG. Inline critical CSS. Preload hero images.

Interaction to Next Paint (INP)

Large JavaScript bundles block the main thread, making the page unresponsive to user interactions. Every click, tap, or keyboard input has to wait for JavaScript to finish executing before the browser can respond.

Fix: code-split your JavaScript so each page only loads the code it needs. Defer non-critical scripts. Use web workers for heavy computation. Keep the main thread clear.

Cumulative Layout Shift (CLS)

JavaScript that injects content into the page after the initial render causes layout shifts. A dynamically loaded ad, a late-appearing navigation bar, or a component that renders after an API call can all push existing content around the screen.

Fix: reserve space for dynamically loaded content using CSS dimensions. Render above-the-fold content server-side so it does not shift. Avoid inserting content above existing content.

How to test JavaScript SEO

Testing is essential because JavaScript SEO problems are invisible to normal browsing. A page can look perfect in your browser while being completely broken for Googlebot.

Google Search Console URL Inspection

The most reliable test. Enter a URL, click "Test Live URL," and compare:

  • HTML tab: shows what Google received from your server (the crawl phase output)
  • Screenshot tab: shows what Google saw after rendering JavaScript
  • More info tab: shows any resource loading errors or JavaScript errors

If important content appears in the screenshot but not in the HTML, you are relying on client-side rendering for that content.

View page source vs. Inspect element

A quick local test: right-click your page and select "View Page Source" (not "Inspect Element"). View Page Source shows the raw HTML your server sends, the same thing Googlebot sees during the crawl phase. Inspect Element shows the DOM after JavaScript has executed. If your content appears in Inspect but not in View Page Source, Google may not see it during the initial crawl.

Disable JavaScript in your browser

Open Chrome DevTools, go to Settings, and disable JavaScript. Reload your page. What you see is approximately what Googlebot sees during the crawl phase (before rendering). If your page is blank or missing content, you have a JavaScript SEO problem.

Lighthouse and PageSpeed Insights

Run a Lighthouse audit to check Core Web Vitals scores and identify JavaScript-related performance issues. Look specifically at "Reduce unused JavaScript" and "Minimize main-thread work" recommendations.

Frequently asked questions

Can Google render JavaScript?

Yes. Googlebot uses a headless Chromium browser that executes JavaScript and renders pages similarly to a real browser. However, rendering happens in a separate queue after the initial HTML crawl, which creates a delay. Complex scripts, third-party dependencies, and client-side rendering can fail or time out during this rendering step. The safest approach is to not rely on Google's JavaScript rendering at all. Send important content and metadata in the initial HTML response using server-side rendering or static generation.

Is React bad for SEO?

React itself is not bad for SEO. The problem is how React is typically configured. A default Create React App setup uses client-side rendering, where the server sends an empty HTML shell and React builds the page in the browser. Search engines see the empty shell first and may not wait for React to render the content. The solution is to use React with a framework that supports server-side rendering, like Next.js. With SSR or static site generation, React pages send fully rendered HTML to search engines while still providing the interactive experience React is known for.

Do I need server-side rendering for SEO?

Not always, but it is the safest approach for content that needs to rank. If your site is primarily a web application where users log in and interact (a dashboard, a SaaS tool), client-side rendering is fine because those pages do not need to be indexed. If your site has public-facing content that should rank on Google (blog posts, product pages, landing pages, documentation), server-side rendering or static site generation ensures Google sees your content immediately without waiting for JavaScript to execute.

How do I test if Google can see my JavaScript content?

Use Google Search Console's URL Inspection tool. Enter a URL, click "Test Live URL," then compare the HTML tab (what Google received from your server) with the Screenshot tab (what Google saw after rendering). If important content appears in the screenshot but not in the HTML, you are relying on client-side rendering. You can also use the site: operator in Google to check if your pages are indexed, and View Page Source in your browser to see the raw HTML your server sends.

Need help making your JavaScript website visible to search engines? I combine front-end development with technical SEO to build sites that are fast, interactive, and fully indexable. Whether you need to migrate from CSR to SSR, fix rendering issues, or build a new site with SEO baked in from the start, I can help. Get in touch to discuss your project.

B
Bikesh Tamang
SEO Specialist & front-end developer in Kathmandu, Nepal, helping businesses rank higher and turn traffic into customers. More about me →