Client-side rendering (CSR) is a way of building web pages where the server sends a near-empty HTML file and a JavaScript bundle, and the browser then runs the JavaScript to build the actual page in the DOM. Server-side rendering (SSR) and static site generation (SSG) do the opposite: the server returns fully-rendered HTML with the real content already in it. For SEO, CSR is risky because Googlebot has to render JavaScript to see your content, and most AI crawlers don't render JavaScript at all — meaning a pure-CSR site can be invisible to ChatGPT, Perplexity, and Claude.
<body>.CSR creates four distinct problems, each of which can be the dealbreaker on its own:
| Strategy | Where HTML is built | Google SEO | AI crawlers | Performance (LCP) |
|---|---|---|---|---|
| CSR (pure) | Browser, after JS executes | Risky — depends on render queue | Invisible | Worst |
| SSR | Server, on every request | Excellent | Excellent | Good (TTFB-bound) |
| SSG (static) | Server, at build time | Excellent | Excellent | Best |
| ISR (incremental static) | Server, periodically rebuilt | Excellent | Excellent | Best |
| Hybrid (SSR shell + CSR widgets) | Server for content, browser for interactive parts | Excellent | Excellent (content is server-rendered) | Good |
| Dynamic rendering (deprecated) | SSR for bots, CSR for users | Works, but Google recommends against | Works for declared bots | Mixed |
<body> is empty or contains only a <div id="root">, your site is pure CSR.curl https://yoursite.com/pagefrom the terminal. If you don't see the page's text content, neither do AI crawlers.Migrate to Next.js (App Router or Pages Router) or Remix. Both render to HTML on the server by default and hydrate on the client. Most components don't need changes — only ones that touch window or localStorage need to be marked client-only.
// Next.js App Router — server component (default)
// Renders to HTML on the server, no JS shipped for content.
export default async function ProductPage({ params }) {
const product = await fetchProduct(params.id);
return (
<article>
<h1>{product.title}</h1>
<p>{product.description}</p>
</article>
);
}Migrate to Nuxt. Nuxt 3 ships SSR by default and supports SSG via nuxt generate. Most Vue components migrate without changes.
Use SvelteKit. SSR is the default; you opt into client-only mode per route.
Enable Angular Universal (now built into the Angular CLI as @angular/ssr). It adds an Express server that renders the app to HTML on each request.
Consider prerendering — a build step that crawls your CSR app with a headless browser and saves static HTML snapshots per route. Tools: Prerender.io, react-snap, Vite SSG. This covers most SEO needs without a framework migration, but it only works for fully static content.
Bad: Page renders with a spinner, then a useEffect calls /api/article/123 and writes the content. Raw HTML is empty.
Good: Fetch the article on the server (Next.js server component, getServerSideProps, Nuxt asyncData) so the HTML response includes the content.
<a href>Bad: Navigation uses onClick handlers and router.push() with no href. Crawlers don't follow.
Good: Use the framework's Link component (Next.js Link, React Router Link, Nuxt NuxtLink) — these output real <a href>. See our crawlable links guide.
Bad: Title and description are written by JavaScript after page load. Crawlers that don't render JS see the default <title>.
Good: Set meta tags on the server. In Next.js use export const metadata or generateMetadata. In Nuxt use useHead in a server context.
Bad: Product specs only render when the user clicks a tab. Crawler never clicks.
Good: Render all content in the HTML; use CSS (or progressive enhancement) to show / hide it. The crawler sees the full content.
Bad:"Google renders JS, we're fine." True for Google — but GPTBot, ClaudeBot, and PerplexityBot don't. You disappear from AI search results.
Good: Make sure your raw HTML contains your real content, not just a JS bundle.
curl -A "Mozilla/5.0 (compatible; GPTBot/1.0; +https://openai.com/gptbot)" https://yoursite.com/page — does the response contain your content?<h1>, body copy, and links are present.For a deeper checklist, see our site crawlability guide.
Yes — Googlebot uses a current Chromium and renders JS. But rendering is queued separately from initial crawling, JS errors silently break indexing, and other crawlers (especially AI fetchers) don't render at all. SSR / SSG remain the recommended approach.
No. React running through Next.js, Remix, or Gatsby produces server-rendered HTML and is excellent for SEO. React running as a pure SPA (Create React App, Vite SPA without prerendering) is the problem.
No. Hydration is React / Vue attaching event handlers to already-rendered HTML. The HTML is delivered fully formed, so crawlers see real content. Hydration only affects user interactivity, not crawlability.
No, generally not. OpenAI, Anthropic, and Perplexity have not committed to JS rendering at scale, and most evidence indicates their crawlers fetch raw HTML only. If you want to be cited in AI answers, your content must be in the initial HTML response.
Google deprecated it as a long-term recommendation in 2022. It still works mechanically — serving SSR HTML to bots and CSR to users — but it's a workaround, not a strategy. Migrate to SSR or SSG when you can.
Static site generation. If your content rarely changes, build the site to static HTML and serve from a CDN. Next.js generateStaticParams, Nuxt nuxt generate, Astro, and 11ty all do this with no server runtime.
It can if you do it wrong (slow database queries on every request). Done right, SSR + caching has lower LCP than CSR because content arrives ready to paint instead of waiting for JS to download and execute.
No. Authenticated pages aren't indexed and aren't cited by AI crawlers — CSR is fine there. The rule applies to public, indexable content.
CSR was an acceptable compromise when SSR frameworks were immature. In 2026 it isn't — Next.js, Nuxt, SvelteKit, and Astro all make server-rendered HTML the default with no developer cost, and AI crawlers have made the cost of CSR much higher than it used to be. View source, run curl, disable JS, and check what your content actually looks like in raw HTML. If it's empty, your SEO and AI visibility are at risk regardless of how good the content would be once rendered.