# SEO-critical pages are not client-side rendered only

- **Pattern:** `ab-002066` (`performance-load.rendering.no-client-only-seo`)
- **Severity:** critical
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002066
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002066)

## Why it matters

Search engines index the HTML delivered by the server. A page that renders all content via client-side JavaScript sends search bots an empty `<div id='root'>` — the product description, pricing, and metadata are invisible until JavaScript executes, which Googlebot may not fully process or may process with a crawl budget delay. This directly harms organic search ranking for the pages most important to business discovery. Beyond SEO, client-side-only rendering means the user sees a blank page until JavaScript loads, parses, and executes — adding 1–4 seconds of blank white screen on slow connections. For SEO-critical pages, this is a dual failure: discoverability and user experience.

## Severity rationale

Critical because CSR-only SEO pages are effectively invisible to search engines, causing permanent ranking loss for the pages most critical to business discovery.

## Remediation

Convert SEO-critical pages from client-side `useEffect` data fetching to server-rendered async components. In Next.js App Router, server components are the default — move data fetching out of `useEffect` and into the component body with `async/await`.

```tsx
// Before — CSR only, invisible to search engines
'use client'
export default function ProductPage({ params }) {
  const [product, setProduct] = useState(null)
  useEffect(() => {
    fetch(`/api/products/${params.id}`).then(r => r.json()).then(setProduct)
  }, [params.id])
  return product ? <ProductDetails product={product} /> : <Spinner />
}

// After — server-rendered, indexed by search engines
export default async function ProductPage({ params }) {
  const product = await fetch(`https://api.example.com/products/${params.id}`, {
    cache: 'force-cache'
  }).then(r => r.json())
  return <ProductDetails product={product} />
}

// Metadata also server-rendered
export async function generateMetadata({ params }) {
  const product = await fetchProduct(params.id)
  return {
    title: product.name,
    description: product.description,
    openGraph: { images: [product.imageUrl] }
  }
}
```

## Detection

- **ID:** `no-client-only-seo`
- **Severity:** `critical`
- **What to look for:** Count all SEO-critical pages (homepage, product pages, landing pages, about, pricing, blog posts). For each, determine the rendering strategy and check whether HTML content and meta tags are present in the server-rendered output (not injected by client-side JavaScript). Look for `useEffect` data fetching for critical content on important pages. Check for pages that do not exist in build output (all CSR). Enumerate: "X SEO-critical pages found, Y are server-rendered." For a deeper analysis of SEO patterns, the SEO Fundamentals Audit (`seo-fundamentals`) covers meta tags, structured data, and crawlability in detail.
- **Pass criteria:** 100% of SEO-critical pages are server-rendered (SSR/SSG). Metadata (title, description, og:image) is available in the HTML before hydration. No more than 0 SEO-critical pages should rely on client-side JavaScript for meta tags. Report: "X of Y SEO-critical pages serve HTML content and meta tags from the server."
- **Fail criteria:** 1 or more SEO-critical pages are CSR-only, with all content loaded via JavaScript. Meta tags (title, description, og:image) are set by client-side JavaScript on any SEO-critical page.
- **Skip (N/A) when:** Site has no SEO requirements (0 public-facing pages — e.g., internal tool, authenticated app with no public landing page).
- **Detail on fail:** `"2 of 5 SEO-critical pages are CSR-only — homepage and /pricing render blank HTML until JavaScript loads"` or `"3 of 3 product pages use useEffect for all content — search engines see empty <div id='root'>"`
- **Remediation:** Use SSR or SSG for SEO pages:

  ```tsx
  // Before — CSR only (bad for SEO)
  export default function Product() {
    const [product, setProduct] = useState(null)
    useEffect(() => {
      fetch('/api/product').then(data => setProduct(data))
    }, [])
    return <>{product && <ProductDetails {...product} />}</>
  }

  // After — SSR (good for SEO)
  export default async function Product({ params }) {
    const product = await fetch(`https://api.example.com/products/${params.id}`, {
      cache: 'no-store'
    })
    return <ProductDetails product={product} />
  }

  // Metadata in Next.js
  export async function generateMetadata({ params }) {
    const product = await fetch(`https://api.example.com/products/${params.id}`)
    return {
      title: product.name,
      description: product.description,
      openGraph: { image: product.image }
    }
  }
  ```
- **Cross-reference:** For SEO impact of client-side rendering, the SEO Fundamentals audit covers indexability and meta tag requirements in detail.

## External references

- iso-25010 performance-efficiency.time-behaviour

Taxons: performance, findability

HTML version: https://auditbuffet.com/patterns/ab-002066
