Googlebot can render JavaScript. It has used an up-to-date version of Chromium since 2019. So why do so many React, Vue and Angular sites still struggle to get indexed? Because rendering is a separate, delayed step, and plenty of common patterns break it or hide content from it.
How Google processes a JavaScript page
- Crawl: Googlebot fetches the URL and reads the raw HTML response.
- Render queue: pages that need JavaScript wait for rendering resources. This can be quick or take a while.
- Render: a headless Chromium runs your JavaScript and produces the final page.
- Index: Google indexes the rendered content and discovers links from it.
If your important content, links and meta tags are already in the raw HTML, Google gets them at step one. If they only exist after step three, you depend on rendering working perfectly, every time.
Common reasons JavaScript sites fail
Links that are not links
Google discovers pages by following anchor elements with an href attribute. Buttons or spans that navigate with a click handler are not followed.
// Not crawlable
<span onClick={() => router.push("/pricing")}>Pricing</span>
// Crawlable
<a href="/pricing">Pricing</a>Content that needs interaction
Googlebot does not click tabs, scroll infinitely or press load more. Content that only appears after interaction may never be seen. Render it in the HTML and hide it with CSS if needed, and give paginated content real URLs.
Hash-based routing
URLs like example.com/#/products are treated as a single page, because everything after the hash is ignored. Use the History API with real paths such as example.com/products.
Blocked resources
If robots.txt blocks your JavaScript bundles or API endpoints the page needs, Google renders an empty shell. Allow the files required to build the page.
Soft 404s and client-side meta tags
Single-page apps often return a 200 status for every URL and show a not found message with JavaScript. Google may index those error pages. Return a real 404 status from the server, and set titles, descriptions and canonical tags in the server response rather than after load.
It is not just Google
Social networks building link previews, many AI crawlers and several smaller search engines do not run JavaScript at all. To them, a client-rendered page is often just an empty div and a script tag.
How to test what Google sees
- In Search Console, use URL Inspection, then Test Live URL and View Tested Page. Check the rendered HTML and screenshot.
- Compare View Page Source, which is the raw HTML, with the Elements panel, which is the rendered DOM. Anything only in the second depends on rendering.
- Check the Page indexing report for Crawled, currently not indexed and soft 404 issues on JavaScript-heavy sections.
- Search Google for a unique sentence from a page in quotes to confirm it was indexed.
The fix: render on the server
- Server-side rendering: the server sends complete HTML for every request, then JavaScript takes over in the browser. Next.js, Nuxt, SvelteKit and Remix make this the default.
- Static generation: pages are built to HTML ahead of time. Ideal for marketing pages, docs and blogs.
- Prerendering services: generate HTML snapshots for an existing single-page app when a rewrite is not possible.
About dynamic rendering
Server rendering also tends to improve LCP, because the hero content is in the HTML from the start. Pair it with LightLoader to handle the hydration-time scripts and you get pages that are both crawlable and fast.