JavaScript is the reason your site looks amazing, and it's the reason a disturbing number of your pages look like blank slates to a crawler. That's not a reason to hate the framework — Next.js is genuinely great, and in 2026 it's the default choice for teams who care about both performance and pragmatism. But it is a reason to understand exactly where the framework delivers HTML and where it leaves the work to the user's browser. Because a crawler is not a user's browser.
This guide is the full JavaScript SEO playbook for Next.js and React: how rendering works, where the traps are, what the modern App Router changes, and the checklist we run on every client site before we call a page crawlable.
The problem: two renders
Here's the core issue. Google can render JavaScript, but it does it as a second, separate pass — it fetches the HTML, then later runs the JavaScript to see what's really there. That second pass is slower, scheduled differently, and doesn't always finish. Bing, and the AI crawlers that now visit your site, are less capable still. Qualcomm's crunching aside, the web's most important readers all want to see content before they execute your bundle.
So the difference between a page that "just works" and one that's effectively invisible comes down to one deceptively simple question: does the important content arrive in the initial HTML response, or does it only exist after a script runs?
What that means in practice
These are the practical stakes — and the exact pitfall our technical SEO engineering service audits for first:
- If your page is client-side rendered (CSR) — a shell of an HTML page with React injecting everything after load — the crawler sees mostly nothing on the first pass. You're gambling on that second render, and sometimes you lose. Content behind tabs, accordions, or infinite scroll is doubly at risk, because even a capable renderer pauses when it has to invent interactions.
- If your page is server-side rendered (SSR) or statically generated (SSG), the meaningful content — the text, the meta, the structure — is in the first HTML bytes. The crawler reads it immediately. No gamble, no delay, no second-pass scheduling lottery.
The fix isn't "stop using React." It's "stop making crawlers wait on the browser."
What the App Router gets right (and what it doesn't change)
Next.js's App Router, built on React Server Components, is the most crawlable default a React site has ever shipped with. When you use a Server Component, the text customers actually came for renders on the server and arrives in the HTML. That's a structural SEO win you used to have to wire up by hand.
What the App Router does not do is render everything for you. The instant you mark a component "use client" it disappears from the server HTML on the initial response and joins the hydration queue. Interactive widgets are the legitimate exception, but the discipline is unchanged: server-render the meaning, client-render only the interaction. And metadata is a case study in this — next/metadata gives you clean, static titles and descriptions that land in the initial HTML, which is exactly where they belong.
The checklist for a crawlable Next.js site
This is the list we run — on new builds and on inherited ones:
- Render on the server by default. Use static generation when content is known at build time, server rendering when it changes, and only hand the client genuinely interactive islands. If you're reaching for
"use client"more than occasionally, you're rebuilding a CSR site on top of a server-rendered one. - Ship metadata server-side. Title, description, and Open Graph should exist in the HTML response, not be injected by a client-side library after the fact. A page whose title renders in the DOM but is missing from the raw HTML looks unfinished to a crawler — and it's the kind of thing that shows up in an indexing report as "duplicate" or "thin" without any obvious cause.
- Don't hide content under interactions. If your key text only appears after a click, a scroll, or a state change that a bot never triggers, it doesn't exist for SEO. Tag-based content, login-gated copy, and client-rendered FAQ accordions are the usual suspects. Progressive enhancement is your friend: render the content, add the accordion behavior on top.
- Watch lazy-loading. Loading critical content "for performance" can accidentally hide it from crawlers. Lazy-load your images, not your meaning. The image below the fold that never loads until scroll is fine; the paragraph that only exists after a fetch isn't — and the same lazy pattern that broke 2019 crawlers still breaks 2026 ones, which is exactly how a page ends up scoring beautifully in the lab while field data tells a different story.
- Verify with a no-JS view. Fetch your page without executing JavaScript and ask: would I understand what this page is about? If not, a crawler is struggling too. The real-world version of this test is a text-mode browsing tool or curl with scripts disabled — not a confident assumption that "Google handles JS."
- Keep sitemaps in the HTML age. Only put URLs in your sitemap that resolve to crawlable HTML. If a sitemap URL returns the bare app shell, you've handed Google a list of pages that all say the same nothing — a recipe for pages stuck in "Discovered currently not indexed" and a crawl budget spent on shells.
The verification drill
Before you trust a template change, run the three checks that separate a claimed crawlable page from a verified one:
- Fetch without JavaScript.
curl -sL "https://yoursite.com/page" | head -c 2000and read what comes back. If the meaningful text and the title are in those first bytes, you're in business. If you get an empty shell and a script tag, the content is client-rendered and you're gambling on the second pass. - Inspect the metadata in the raw response. Confirm the title, description, and Open Graph tags exist in the HTML itself, not in a
<head>injected after hydration. - Compare raw vs rendered. Fetch with scripts disabled and again with a headless browser. Meaningful content should appear in both. If it doesn't, treat every page that hides content behind that difference as invisible to a substantial share of the crawling ecosystem.
A page that passes all three rarely needs a second render pass to be understood and never belongs on the "needs indexing help" list. If your site fails the drill and the ranking problems don't respond to effort, you're past JavaScript — you're looking at a site that doesn't enter the index cleanly, and the diagnosis broadens far beyond rendering. We usually find the full set of suspects in why a new website isn't ranking.
The performance connection
The same choices that make a site crawlable make it fast, and this matters twice: once for real users and once for what crawlers are willing to spend on you. A client-rendered page forces every crawler to download the full bundle before it can read a word — expensive for them, expensive for your server bills, and slow to index. A dramatically simpler mental model unites all of it: meaningful HTML in the first response. That's the same principle that drives Core Web Vitals — LCP (when the content paints), INP (how responsive the page is with real JavaScript attached), and CLS (how stable it stays while your scripts hydrate). We cover the full metric-by-metric breakdown separately, but the short version is that "crawlable" and "fast" are the same architecture viewed from two sides. Server Components don't just win the SEO audit; they win the Lighthouse audit and the real-user report.
The rendering decision table
For each page, the decision is a short table, not a philosophical debate:
| Content type | Rendering strategy | Why |
|---|---|---|
| Marketing pages, blog posts, docs | Static (SSG) | Content is known at build time; rebuild on publish |
| Product catalogs with frequent changes | ISR or SSR | Fresh data without sacrificing crawlability |
| Personalized dashboards | SSR or client-rendered | Keep the meaning in HTML; render the dashboard client-side |
| Login-protected tools | Client-rendered | Crawlers can't see them anyway; don't ship the shell to the index |
The only rule that matters is the last column: if a page deserves indexing, its content belongs in the first response.
Common mistakes we still see in audits
- Metadata via client-side libraries. The old
react-helmetcrowd moved to App Router and forgot to migrate their pattern. If your title and description only exist after React runs, your pages are effectively untitled for crawlers reading raw HTML. - Hydration that blocks interactivity. Huge bundles that stall the main thread turn crawlable pages into slow ones — and slow pages get crawled less. The INP fight and the SEO fight are the same fight.
- Infinite scroll with no fallback. Products load when the user scrolls, and the crawler never scrolls. If you must infinite-scroll, provide a paginated HTML fallback path the crawler can follow.
- The "we'll submit it to index" crutch. Manually requesting indexing doesn't rescue pages that require the render pass to contain anything. Fix the render, and the indexing request becomes unnecessary — genuinely crawlable pages get found through internal links and sitemaps, which is the flow Google prefers.
The mental model
People, crawlers, and performance all want the same thing: the important stuff up front. When you optimize for "meaningful HTML in the first response," you get a site that loads fast for users, renders clean for bots, indexes without babysitting, and never bets a render budget on a second pass.
JavaScript isn't the enemy. Architecture that makes crawlers gamble is. And that's a choose-your-render-strategy problem, solvable in Next.js cleanly — and unsolvable by plugins, pre-render hacks, or hoping.
For a deeper dive, see our guide on site architecture for topical authority.
We build and fix Next.js sites at Launch at Dawn, and we test them the way crawlers actually read them — raw HTML first, JavaScript second, field data always. That rendering discipline is the same one behind our Next.js eCommerce work. book a free website teardown and we'll show you what your own pages look like to a bot that isn't running your JavaScript, plus the two or three rendering changes that would fix what it reveals.