ssrchecker
HomeCheckFrameworksNext.js

Is your Next.js page actually server-rendered?

Short answer
Next.js server-renders by default in both routers, so most pages return complete HTML. Adding "use client" does not turn that off. Pages come back client-rendered mainly when their main content is fetched inside useEffect or a client data-fetching hook, which runs only after hydration.

That default is why the Next.js diagnosis is usually more subtle than a plain React SPA. The page is rarely empty — it comes back as a server-rendered shell with the parts that matter missing, which reads as HYBRID rather than CSR. That verdict is easy to dismiss and is normally the one worth acting on.

Check your Next.js page
https://

What "use client" actually does

This is the most common misunderstanding in Next.js SEO, and it costs people real time. "use client"does not mean "render this in the browser only". Client components are still prerendered on the server into HTML. The directive marks a boundary for hydration and interactivity, not for rendering.

A page with "use client" at the top and content in its JSX will pass this check with a green verdict. What breaks SEO is not the directive — it is fetching data inside the component after mount. The directive is a prerequisite for that pattern, which is why the two get blamed together.

The distinction
Fine

A client component that receives its data as props from a server component. The data is resolved before the HTML is sent.

Breaks

A client component that calls an API inside useEffect and renders a spinner until it resolves. Crawlers capture the spinner.

App Router and Pages Router fail differently

Both routers server-render by default, but the way they break is not the same, and the fix differs accordingly.

In the App Router, components are server components unless marked otherwise. The failure mode is a server component that renders a client child which then fetches the real content. The page looks server-rendered — the header, nav and footer are all in the HTML — while the product grid or article body is absent. That produces a HYBRID verdict with a high content ratio, which is exactly the result people skim past.

In the Pages Router, the split is more explicit. A page with getServerSideProps or getStaticProps resolves its data before the HTML is sent. A page with neither, doing its fetching in useEffect, is effectively a client-rendered route inside an otherwise server-rendered app. Because it is per-page, you can have some URLs indexing well and others not indexing at all in the same deployment — which is usually what sends people looking for a checker in the first place.

Side-by-side comparison of raw HTML and rendered DOM for a Next.js App Router page where the product grid is missing before JavaScript runs
A typical HYBRID result — layout present, the content that matters absent.

SSG, SSR and ISR all pass this check

A point worth being clear about, because it causes unnecessary re-architecture: this tool cannot distinguish static generation from server-side rendering, and neither can Googlebot. Both deliver complete HTML on the first request. So do incrementally regenerated pages.

The choice between them is about data freshness and infrastructure cost, not about SEO. If someone is proposing a move from getStaticProps to getServerSideProps for ranking reasons, that is not a rendering fix — and it will make your pages slower to serve.

Your configuration and the verdict to expect

Find your setup below. If the tool gives you something different from the expected verdict, that difference is the bug.

SetupExpectWhy
App Router, server component, no client fetchingSSRContent is in the HTML. This is the default and the good case.
App Router, page.js marked "use client"SSRStill prerendered on the server. "use client" does not disable SSR.
App Router with export const dynamic = "force-dynamic"SSRRendered per request. Still complete HTML.
App Router with revalidate (ISR)SSRServed from a cached prerender. Crawlers get full HTML.
Pages Router with getServerSidePropsSSRProps resolved server-side before the HTML is sent.
Pages Router with getStaticPropsSSRBuilt at build time. Fully present in the HTML.
Any route fetching its main content in useEffectHYBRID or CSRThe shell renders server-side, the content arrives after hydration.
Client component with a loading skeleton and SWRHYBRIDCrawlers see the skeleton, not the data.
Static export with all data client-fetchedCSROnly the shell exists in the HTML.

Why a Next.js page comes back CSR or HYBRID

In rough order of how often each one turns out to be the cause:

01
Client-side data fetching for primary content
The page shell is server-rendered but the article body, product list or search results arrive after hydration via useEffect, SWR or React Query. Move the fetch into a server component or a data-fetching function.
02
A loading state that never resolves for crawlers
Skeletons and spinners are in the HTML; the content that replaces them is not. Whatever the skeleton is hiding is what the crawler sees instead of your content.
03
Content gated behind an effect-driven condition
Tabs, accordions and 'load more' sections that only mount client-side. If it matters for ranking, render it in the HTML and hide it with CSS instead.
04
Middleware or auth redirecting non-browser requests
The raw fetch gets a redirect or a challenge while the rendered fetch succeeds. The tool reports this as a crawler block rather than a CSR verdict.
05
A third-party widget owning a whole section
Reviews, listings or search widgets that inject content from another origin. That content is not yours in the crawler's eyes regardless of rendering mode.

Metadata, canonicals and the title mismatch

A separate failure worth checking even when the body content passes. If Google shows a title that is not the one you set, the title is probably being written client-side.

The Next.js metadata export and generateMetadata both run on the server and put tags in the initial HTML — that is the correct approach. Setting document.title in an effect, or using a client-side head library carried over from an older React setup, means the tags are not there when the raw HTML is captured. The check reports title, description and canonical separately from body content for exactly this reason.

Next.js rendering FAQ

Yes. In the App Router every component is a server component unless it is marked with the use client directive, and pages are prerendered to HTML on the server. A page only fails a rendering check when its main content is fetched client-side after hydration.