← All framework guides

React SEO: 8 checks and how to fix them

These are the React-specific checks RankCLI runs, and the fix for each one in React's own idiom. React renders on the client by default, which is where most of its SEO problems come from.

8 checks
2 rated error
spa
Client-rendered by default

Run all of these against your own site, free and without an account:

npx @rankcli/cli audit -u https://your-site.dev

Where React makes SEO harder

  • •Client-side rendering means empty HTML for crawlers
  • •Dynamic content invisible to search engines
  • •Slow First Contentful Paint
  • •No server-side meta tag generation
  • •JavaScript-dependent routing breaks crawlability

The checks

error
REACT_CSR_DETECTED

Client-side rendered React app detected

React apps without SSR send empty HTML to crawlers. Search engines may not index your content.

Option 1: Migrate to Next.js for automatic SSR/SSG Option 2: Add prerendering with react-snap or prerender.io Option 3: Use react-helmet-async with a prerender service

error
REACT_NO_SSR_META

Meta tags not server-rendered

Title and meta description are set client-side and won't be seen by crawlers.

For Vite/CRA: Use a prerender plugin or migrate to Next.js

Quick fix with vite-plugin-ssr:

// pages/+Head.tsx
export function Head() {
  return <>
    <title>Your Title</title>
    <meta name="description" content="Your description" />
  </>
}
warning
REACT_HYDRATION_MISMATCH

Hydration mismatch detected

Server HTML differs from client render, causing flicker and potential SEO issues.

Common causes:

  1. Using Date.now() or Math.random() in render
  2. Browser-only APIs like window.localStorage
  3. Different data on server vs client

Fix: Use useEffect for browser-only code:

const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
if (!mounted) return null; // or skeleton
warning
REACT_HELMET_NOT_ASYNC

Using react-helmet instead of react-helmet-async

react-helmet has memory leaks and doesn't work properly with SSR.

Replace react-helmet with react-helmet-async:

npm uninstall react-helmet
npm install react-helmet-async

Update imports:

// Before
import { Helmet } from 'react-helmet';

// After
import { Helmet, HelmetProvider } from 'react-helmet-async';

// Wrap app in provider
<HelmetProvider>
  <App />
</HelmetProvider>
warning
REACT_ROUTER_NO_SSR

React Router without server-side support

Client-side routing breaks when crawlers access URLs directly.

Ensure your server returns the same HTML for all routes.

For static hosting (Netlify, Vercel): Add a redirect rule to serve index.html for all routes.

netlify.toml:

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200
warning
REACT_NO_PRERENDER

No prerendering configured

Static pages could be prerendered for better SEO and performance.

Add react-snap for build-time prerendering:

npm install react-snap

package.json:

{
  "scripts": {
    "postbuild": "react-snap"
  },
  "reactSnap": {
    "source": "build"
  }
}
warning
REACT_BUNDLE_TOO_LARGE

React bundle too large

Large JavaScript bundles delay interactivity and hurt Core Web Vitals.

  1. Use React.lazy() for code splitting:
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));

<Suspense fallback={<Loading />}>
  <HeavyComponent />
</Suspense>
  1. Analyze bundle with source-map-explorer:
npm install source-map-explorer
npx source-map-explorer build/static/js/*.js
notice
REACT_NO_SUSPENSE

Not using Suspense for data fetching

Without Suspense, content pops in causing layout shifts (CLS).

Use Suspense with a data fetching library:

<Suspense fallback={<Skeleton />}>
  <DataComponent />
</Suspense>

Check your own React site

280+ checks including every one above. No signup, nothing leaves your machine.

Run a free audit