Crawler and indexing behaviour for JavaScript sites has changed several times, and much confident advice about it is out of date. The old claim that Google cannot read client-rendered content is no longer accurate: Google Search runs JavaScript with an evergreen build of Chromium, and queues pages separately for crawling and for rendering. The current problems are narrower: what the crawler receives first, what it can follow, and which URL your site nominates.
This article works from current official documentation rather than older rules of thumb, because the difference matters when choosing a rendering strategy or planning a migration. Where a claim is uncertain it is marked as such.
Rendering strategy and what search visibility depends on
Google's guidance is that server-side rendering, static rendering and hydration are the supported approaches, and that dynamic rendering, where a server detects crawlers and serves them a pre-rendered variant, is a workaround rather than a recommended solution.
The useful reading is not that client rendering is disqualifying, but that the initial HTTP response is what every crawler and reader sees first and what you control most cheaply. Google's JavaScript documentation puts it plainly: server-side or pre-rendering is still a good idea, because it makes the site faster and not all bots run JavaScript.
Server rendering
A request produces complete HTML: the baseline that works for every consumer, and the right default for anything whose content is why the page exists. The cost is a server round trip per uncached request.
Static generation and incremental regeneration
Pages are generated ahead of time and served as files, then refreshed on a schedule or on demand. In Next.js this is Incremental Static Regeneration: a revalidated page is served from cache while regeneration happens in the background, optionally triggered by a content-management webhook. For search the question stays the same: what does the crawler receive? Complete HTML with the content in it, not a shell that fills in later.
One detail is easy to get wrong: a page whose metadata function runs at request time can become dynamic even when the body is static. If canonical URLs or robots directives depend on per-request state, check what that does to the rendered response.
Streaming
A static shell can be sent immediately while slower parts of the response stream in behind suspense boundaries. The same question applies to what the crawler receives: content below a boundary may not be in the initial response, so anything indexable should not sit behind a boundary that can delay indefinitely. Google also documents that on encountering a noindex directive it may skip rendering and JavaScript execution entirely, so a noindex in the initial HTML cannot be reversed later from the client.
The risks that are actually specific to client-only content
Not "client rendering will hurt your rankings". Four concrete failure modes, each documented.
- Content gated behind user interaction. Google's crawlers generally do not click buttons and do not trigger JavaScript functions that require a user action to update the page. Anything reachable only that way may not be seen.
- State that does not persist across loads. Google's Web Rendering Service does not retain state between loads: local storage, session storage and HTTP cookies are cleared. A page reading tenant, locale or consent state from the client renders differently for the crawler than for a returning visitor.
- Non-HTTP transports. Googlebot retrieves content over HTTP and does not support WebSockets or WebRTC. Content delivered only over those transports needs an HTTP path.
- APIs the rendering environment lacks. Some browser APIs cannot be polyfilled, so feature-detect anything critical and provide a fallback path that renders the content without it.
Two further points are easy to overlook. Links are discovered from the href attribute of anchor elements, so injecting them with JavaScript works only if they are real anchors. And URL fragments are not usable for routing: Google's documentation is explicit that it ignores them, which is why the legacy AJAX-crawling scheme has been deprecated since 2015.
Client-side rendering also creates the soft 404 problem: a single-page application whose error state is handled in JavaScript frequently returns a 200 for a page that does not exist, and those pages can be indexed as real content. Google's documented mitigations are to redirect to a URL the server answers with a genuine 404, or to serve noindex on error pages.
Crawlability: status codes, links and what the crawler can reach
Crawlability is mostly a function of things that are cheap to get right. Use meaningful HTTP status codes, because they are how a crawler learns that something moved or does not exist, and link pages with real anchors. Do not use robots.txt to remove a URL from the index: a disallowed URL can still appear in results as a URL-only listing if other pages link to it. Use a noindex directive instead, on a page the crawler can fetch.
Caching interacts with crawling in a way that surprises people: Google's rendering service may ignore caching headers and use outdated JavaScript or CSS. Content fingerprinting in filenames, such as main.2bb85551.js, avoids that, because a changed file gets a changed name.
Sitemap inclusion is a weaker canonicalization signal than a redirect or a canonical link, which is why the latter two are worth getting right first.
Canonical URLs on a JavaScript site
Google's guidance on canonicalization is that redirects and rel=canonical annotations are strong signals, sitemap inclusion is weak, and these stack.
- Include a self-referential canonical on the canonical page itself.
- Do not specify different canonical URLs for the same page using different techniques. A sitemap entry and a rel=canonical tag that disagree create a conflict.
- Do not use robots.txt for canonicalization, and do not use the URL removal tool for it.
- Do not specify a fragment as canonical; Google generally does not support fragments.
On rendering: Google's documentation recommends HTML for the canonical tag. If it must be injected with JavaScript, set it to the same value as the original HTML and make sure it is the only rel=canonical tag on the page.
Pagination
Pagination is where well-built sites most often contradict the documented guidance, because a self-canonical page-one policy feels tidy and is wrong here. Google's guidance is unambiguous: give each page its own canonical, and do not make page one the canonical for the rest.
- Give each page a unique URL, for example with a page number query parameter. Paginated URLs are treated as separate pages.
- Give each page its own canonical URL, self-referential. Page one canonicalises to itself, not to the collection.
- Link pages sequentially with anchor elements carrying href, and consider linking every page back to the first so its position in the sequence is clear.
- Do not use fragments for page numbers. Google ignores fragment identifiers, so it may treat a fragment-only difference as a page it has already fetched.
- Do not add rel=next or rel=prev expecting them to work. Google no longer uses those tags, though other search engines still might.
One permission is worth knowing: pages in a paginated sequence do not need distinct titles and descriptions, a documented exception to the advice about unique metadata.
Faceted and filtered URLs
Filters, sort orders and both together can generate a very large number of URLs describing the same underlying list. Google's faceted navigation guidance turns on one question: do these URLs need to appear in Search? If yes, keep them crawlable and follow the URL conventions that guidance sets out. If no, prevent crawling, which saves server resources and keeps crawler time off combinations nobody wants in results.
The two controls act at different stages. robots.txt governs crawling: a disallow rule stops the fetch, and a page that is never fetched will not have its noindex read. noindex governs indexing, and works only when the crawler can reach the page and see the directive. So: noindex on an accessible page, robots.txt for URL patterns you do not want fetched at all. One consequence is worth stating plainly: a robots.txt-blocked URL can still appear in search results when other pages link to it, as a URL-only listing without the page's content, which is not the same as being indexed.
The practical design rule is to decide which parameters are part of a page's identity and which are not, then encode that in one place. A filter UI added later without updating the allowlist, the canonical handling and the robots directive together is how a site ends up with millions of variants.
Structured data that matches the visible content
Structured data guidelines are specific about one thing: do not mark up content that is not visible to readers of the page. Google's wording is that if JSON-LD describes a performer, the HTML body must describe that same performer. The same documentation states that valid markup is not a promise of a rich result.
- JSON-LD is the recommended format of the three supported ones, and it can be injected with JavaScript. Test the result rather than assuming the injection worked.
- Do not block pages carrying structured data with robots.txt or noindex, which makes the markup undiscoverable.
- Put the markup on the page it describes. For duplicate pages, Google's guidance is to place the same structured data on all of them, not only on the canonical.
- Every image URL referenced must be crawlable and indexable, or it cannot be found and displayed.
- Verify with the Rich Results Test and the URL Inspection tool. For JavaScript specifically, Google's tooling shows loaded resources, console output, exceptions and the rendered DOM, which is how you confirm what the crawler received.
That last point is the practical one for this whole article: the tooling that settles a JavaScript SEO question inspects what Google's renderer produced for your URL. The Core Web Vitals article covers the performance side, and the Next.js versus traditional CMS article covers how rendering strategy changes the framework-versus-CMS question.
Migration risks
A migration that changes rendering strategy changes what a crawler receives on the day it ships, even when the visible page looks identical. The risks are mostly enumerable.
- Silent index loss. URLs that stop returning complete HTML can drop out of the index before being re-discovered. The documentation specifies no duration, and treating any timescale as reliable would be inventing it.
- Redirect chains and loops introduced by the new routing, which waste crawl budget and can collapse canonical signals.
- Status code regressions, especially the 200-instead-of-404 pattern from client-side error handling.
- Metadata differences. A template that renders canonical URLs differently between build-time and request-time paths can emit conflicting tags.
- Structured data emitted only on the client, which can drop out silently when the injected markup fails.
A pre-launch checklist
Ordered so that the items which are expensive to reverse come first.
- Confirm the target URL space is final, and record the mapping from every old URL to its new one.
- Verify that indexable pages return complete HTML with content in the initial response, checking the actual response rather than the build output.
- Confirm every indexable page has one self-referential canonical, and that no other technique declares a different canonical for it.
- Check paginated pages each canonicalise to themselves, and that pages link sequentially with real anchors.
- Confirm filtered URLs resolve to a deliberate policy: indexed with a unique canonical, or excluded, with no page left to chance.
- Validate structured data with the Rich Results Test and confirm it matches the visible content.
- Verify real status codes: 200 for content, 404 for missing, and a redirect for anything moved.
- Submit the sitemap and confirm it contains the URLs you intend and excludes the ones you do not.
- Run the URL Inspection tool on a sample drawn across each template type, rather than the homepage alone.
- Keep the redirect map and the old URL inventory for as long as external links can still point at them.
Step nine finds template-specific problems: homepages and article pages behave differently from listing and filtered pages, so a sample drawn across template types catches what a homepage check never will.
What to monitor after migration
Verification does not end at launch, because the failure modes above are quiet.
- Search Console Page Indexing and Crawl Stats reports, for monitoring Googlebot and its rendering service activity.
- Coverage of indexable URL types in the sitemap, tracked as a proportion rather than an absolute.
- Server logs for crawler status codes. A spike in 404s after a routing change is the earliest visible signal of a broken mapping.
- A canonical audit across each template type, on a schedule rather than once, because a template change can reintroduce a fixed conflict.
- Field performance data for the templates that changed, since the rendering change usually moves it.
Two things to state plainly. The documentation promises no recovery timeline after an indexing drop, so a plan assuming a fixed number of days is an assumption rather than a citation. And none of these measures are ranking controls: they establish whether the site is crawlable, correctly canonicalised and correctly marked up.
Rendering strategy is usually decided in a wider architecture conversation, before any migration is scoped. The services page covers how we scope that work; contact is where to describe a specific migration you are weighing.


