/ JS Checker Fixes / Crawler Rendering

How to Fix JS Crawler Rendering

Search engines render JavaScript imperfectly. Googlebot's Web Rendering Service queues your page after initial fetch, processes it days later (sometimes weeks), and may fail on slow scripts, infinite scroll, or scripts blocked by privacy settings. Other crawlers (Bing, social media unfurlers, AI bots) render JS less reliably. If critical content depends on JS execution, your indexing is fragile. The fix: server-render or prerender what crawlers need to see.

1. Test what crawlers see

Step 1
URL Inspection in Search Console
Search Console → URL Inspection → paste a URL → "Test Live URL" → "View Rendered HTML". Shows the HTML Googlebot actually rendered. Compare to what users see.
Step 2
Mobile-Friendly Test
search.google.com/test/mobile-friendly shows rendered HTML and screenshot. Faster than URL Inspection for quick checks.
Step 3
Curl to see raw HTML
curl -A "Mozilla/5.0 (compatible; Googlebot/2.1)" https://yoursite.com/page | grep -i "<title>"
Raw HTML from the server. This is what Googlebot indexes BEFORE rendering. Title, meta description, structured data should be present here.

2. Identify the gaps

Common gaps between raw HTML and rendered HTML:

⚠️ Anything not in raw HTML depends on Google successfully rendering JS to be indexed. That dependency causes indexing delays, missed indexing, and competitive disadvantage vs sites with SSR.

3. Server-side render critical content

Next.js

// pages/product/[id].js
export async function getServerSideProps({ params }) {
  const product = await fetchProduct(params.id);
  return { props: { product } };
}

export default function ProductPage({ product }) {
  return (
    <>
      <Head>
        <title>{product.name} - Brand</title>
        <meta name="description" content={product.description} />
      </Head>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
    </>
  );
}

Output HTML contains title, meta, and content from getServerSideProps. Googlebot indexes immediately.

Nuxt 3 (Vue)

<script setup>
const route = useRoute();
const { data: product } = await useFetch(`/api/products/${route.params.id}`);

useHead({
  title: () => `${product.value.name} - Brand`,
  meta: [{ name: 'description', content: product.value.description }]
});
</script>

<template>
  <h1>{{ product.name }}</h1>
  <p>{{ product.description }}</p>
</template>

SvelteKit

// +page.server.js
export async function load({ params }) {
  return { product: await fetchProduct(params.id) };
}

// +page.svelte
<svelte:head>
  <title>{data.product.name} - Brand</title>
  <meta name="description" content={data.product.description} />
</svelte:head>

<h1>{data.product.name}</h1>
<p>{data.product.description}</p>

4. Prerender static routes

For content that doesn't change per user, prerender at build time. Faster than SSR, cheaper to host (just static HTML).

Next.js Static Generation

export async function getStaticPaths() {
  const products = await fetchAllProducts();
  return {
    paths: products.map(p => ({ params: { id: p.id } })),
    fallback: 'blocking'
  };
}

export async function getStaticProps({ params }) {
  const product = await fetchProduct(params.id);
  return { props: { product }, revalidate: 3600 };
}

Nuxt prerender

// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    prerender: {
      routes: ['/', '/about', '/pricing']
    }
  }
});

Astro (static by default)

---
// Astro file: pages/product/[id].astro
export async function getStaticPaths() {
  const products = await fetchAllProducts();
  return products.map(p => ({
    params: { id: p.id },
    props: { product: p }
  }));
}
const { product } = Astro.props;
---
<html>
  <head>
    <title>{product.name}</title>
  </head>
  <body>
    <h1>{product.name}</h1>
  </body>
</html>

5. Fix hydration mismatches

Hydration is when client JS attaches event handlers to server-rendered HTML. If client and server output differ, you get warnings and possibly broken interactivity.

Common causes

// Mismatch: Date.now() differs between server (render time) and client (hydration time)
function Footer() {
  return <p>Year: {new Date().getFullYear()}</p>;
}

// Fix: render on client only, or pass server value down
import { useEffect, useState } from 'react';
function Footer() {
  const [year, setYear] = useState(null);
  useEffect(() => setYear(new Date().getFullYear()), []);
  if (!year) return null;
  return <p>Year: {year}</p>;
}
// Mismatch: window/document not available on server
function Component() {
  const isMobile = window.innerWidth < 768; // ReferenceError on server
  return isMobile ? <Mobile /> : <Desktop />;
}

// Fix: check for typeof window OR use a hook
function Component() {
  const [isMobile, setIsMobile] = useState(false);
  useEffect(() => {
    setIsMobile(window.innerWidth < 768);
  }, []);
  return isMobile ? <Mobile /> : <Desktop />;
}

6. Dynamic rendering as a last resort

If you can't SSR or prerender, dynamic rendering serves prerendered HTML to crawlers and SPA to users. Rendertron, Prerender.io are common services.

// nginx — detect crawler User-Agent and proxy to prerender service
map $http_user_agent $is_crawler {
  default 0;
  ~*Googlebot 1;
  ~*Bingbot 1;
  ~*facebookexternalhit 1;
}

server {
  location / {
    if ($is_crawler = 1) {
      proxy_pass https://service.prerender.io;
    }
    try_files $uri /index.html;
  }
}

Google permits this but warns against differences between crawler and user content. Prefer SSR or prerendering when possible.

7. Verify the fix

Step 1
Re-test in URL Inspection
Critical content should now appear in raw HTML view, not just rendered HTML view. "Indexing allowed" should be green, no rendering issues reported.
Step 2
Request re-indexing
URL Inspection → "Request Indexing" for important pages. Google re-fetches and re-renders. Within days, the updated indexing reflects in search.
💡 The frameworks designed for SSR (Next.js, Nuxt, SvelteKit, Astro) make crawler-friendly rendering automatic. Sticking with create-react-app or pure SPA approaches is a choice to fight this battle manually. If SEO matters, pick a framework with SSR built in.

⚙️ Re-run the JS Checker

Verify crawler rendering issues are cleared.

Run JS Checker →
Related Guides: JS Checker Fixes  ·  Fix Render-Blocking JS  ·  Agent Compat Fixes  ·  JS Checker Guide
💬 Got a problem?