Contents

A JavaScript site loses indexing when content, links and meta tags appear only after scripts run in the browser. Google renders JavaScript with a delay, and other search engines and AI bots do it worse or not at all. The fix is serving ready HTML from the server, plus real URLs, real links and correct status codes.

Here is how search engines handle JS sites and which mistakes most often cost traffic.

How do search engines index JavaScript sites?

Search engines index JS sites in two stages: first they fetch the server's HTML and index what is in it, then, if resources allow, they run the JavaScript and fill in the picture. Time can pass between the stages, and some crawlers skip the second one entirely.

What that means in practice:

  • if the main text exists only after JS runs, indexing is delayed or incomplete;
  • links that appear after a click or scroll may never be found;
  • meta tags set by scripts are not always taken into account;
  • bots collecting data for AI assistants often do not execute JavaScript at all.

So the rule is simple: everything that should be searchable must be in the server's HTML response.

Which rendering approach should you choose?

Choose the rendering approach by how often content changes and whether it needs indexing. Public pages suit server-side rendering or static generation; private sections are fine with plain client-side rendering.

Approach How it works Suits
CSR (client-side rendering) The server sends an empty shell; JS builds the page Dashboards and admin panels — nothing that is indexed
SSR (server-side rendering) HTML is built on the server per request Catalogues, product pages, frequently changing content
SSG (static generation) HTML is built at build time Landing pages, docs, rarely changing pages
ISR / SWR Static pages refreshed on a schedule or in the background Blogs, catalogues with moderate change rates

We compared frameworks that provide these modes in Nuxt vs Next.js.

A monitor displaying a web page Photo: Unsplash

Which mistakes in a JS app block indexing?

Indexing is blocked not only by missing server rendering but also by mistakes in the app itself: hash-based URLs, button "links", soft 404s and content that loads only on scroll. You can make them even in an app with SSR.

Every page needs its own URL without a hash fragment that opens on direct navigation. Navigation must use real links a crawler can follow:

<!-- Bad: the crawler will not see a link -->
<span @click="router.push('/catalog/shoes')">Shoes</span>

<!-- Good: a normal link; the router intercepts the click itself -->
<a href="/catalog/shoes">Shoes</a>

Vue and React provide RouterLink, NuxtLink and Link components for this — they render a real <a href>.

Status codes and soft 404s

A single-page app often responds 200 to any URL and shows "page not found" via script. The search engine sees thousands of empty pages returning 200 — soft 404s that clutter the index. The server must return a real 404 for missing pages and a 301 for moved ones.

Content on scroll

An infinite feed without normal pagination hides everything below the first batch from crawlers. The fix is pagination with separate URLs (?page=2) and links to them, with infinite scroll layered on top for users.

How do you check what the search engine sees?

You can check what a search engine sees in three ways: look at the raw server HTML, open the page with JavaScript disabled, and use the URL inspection tools in webmaster consoles. If the main text is missing in the first two, you have a problem.

A quick check from the terminal:

# Is the article heading in the server response?
curl -s https://example.com/blog/some-article | grep -o '<h1[^>]*>.*</h1>'

What else to check:

  1. title, description, canonical and hreflang are in the server response, not only after JS runs.
  2. A non-existent URL returns 404.
  3. The sitemap lists all public pages.
  4. In Google Search Console's URL Inspection tool, the rendered page matches what users see.

When does a JS site not need SSR?

SSR is not needed for sections that should stay out of search: dashboards, admin panels, internal tools and checkout steps. A plain single-page app is simpler there, cheaper to host and puts no rendering load on the server.

A good pattern for most products is mixed: public pages are server-rendered or pre-generated, private sections run as an SPA. Modern meta-frameworks let you set this per section. The rest of the pre-launch checks are in our technical SEO checklist.

In short

  • Everything meant for search must be in the server's HTML, not appear after JavaScript runs.
  • Use SSR or static generation for public pages; CSR is fine for private sections.
  • Use URLs without hash fragments, real <a href> links and paginated URLs.
  • Return real 404 and 301 codes instead of soft errors.
  • Check raw HTML with curl and the URL inspection tool in your webmaster console.

FAQ

  • Do search engines other than Google render JavaScript?

    Some do, with varying completeness and speed, and many AI crawlers do not render JavaScript at all. Serving the main content as server HTML is the reliable approach.

  • Is dynamic rendering for bots a good idea?

    It is a workaround in which a separate page version is prepared only for bots. Google recommends server-side rendering or static generation instead, since they serve the same content to people and crawlers.

  • Do we have to rewrite an SPA to improve SEO?

    Not necessarily in full. It is often enough to move public pages to a meta-framework with SSR or into a separate site, keeping the dashboard as a single-page app.