How to Do SEO for a JavaScript Website (React, Next.js, Vue)

On this page

JavaScript frameworks rank fine. The framework is not the enemy; the rendering method is. Googlebot renders JavaScript in a delayed, resource-limited second pass that can fail, so content delivered as server-rendered HTML is reliable while content that only appears after client-side JavaScript runs risks never being indexed. Server-side rendering, static generation, and incremental static regeneration put the content in the HTML where Googlebot sees it on the first pass. Client-side rendering puts it behind a render that may time out. The discipline is to confirm what Googlebot actually receives with URL Inspection rather than assuming the page that loads in your browser is the page Google indexes.

The two-wave model

Googlebot processes a JavaScript page in two stages. First it crawls and reads the raw HTML, extracting whatever content, links, and structured data are present in the source. The page is then queued for rendering, and later an evergreen Chromium-based Web Rendering Service executes the JavaScript and indexes the resulting DOM. That second pass is delayed, runs under timeouts and resource limits, and can fail silently on heavy or slow scripts. Anything not in the DOM when rendering completes risks not being indexed.

This is the core mechanism behind nearly every JavaScript SEO problem. If critical content exists only after client-side JavaScript fetches and injects it, you are betting indexing on a render that Google performs on its own schedule and may not complete as you expect.

The rendering spectrum and SEO risk

Where a page falls on the rendering spectrum determines its risk:

  • Static generation (SSG) and server-side rendering (SSR): the server delivers complete HTML, so content is present on the first pass. Lowest risk.
  • Incremental static regeneration (ISR): static HTML served and regenerated in the background on an interval. Low risk, with content in the HTML.
  • Client-side rendering (CSR): the server sends a near-empty shell and the browser builds the page with JavaScript. Highest risk, because content depends entirely on the second-pass render.

Framework defaults matter. Next.js, Nuxt, and Gatsby ship SEO-friendly server-rendered or statically generated output by default, and the Next.js App Router renders on the server unless you opt a component into the client. Plain client-side React from a tool like Create React App, Vue with a default client-only setup, and Angular without server-side rendering configured all default to CSR and need a server-rendering or static option turned on. These defaults evolve, so confirm the current behavior for the exact framework and version in front of you rather than trusting an older guide.

Verify rendering, do not assume

“It works in my browser” is the trap. A developer can see a fully populated page while Googlebot sees an empty container, because the browser patiently waits for an API call that Googlebot’s renderer may time out on. The only ground truth is the rendered HTML Google actually produces.

Use Search Console URL Inspection, then Test Live URL, and view the rendered HTML and screenshot. Confirm that the critical content (the main copy, headings, links, structured data) is actually present. Check the reported JavaScript console errors and any failed resource loads, since a blocked script or a failed fetch can leave content missing. Compare the raw view-source HTML against the rendered DOM to see exactly what JavaScript added and whether it added the parts that matter. Making this check a habit on important templates catches the highest-cost failures before they cost rankings.

API-loaded content and where to fetch it

The most common silent failure is content loaded client-side from an API after the page renders: reviews, testimonials, listings, prices, related items. That content frequently does not appear in Googlebot’s rendered HTML, so it never gets indexed and never contributes to rankings, even though every human visitor sees it.

The fix is to fetch critical content on the server so it ships in the initial HTML. In Next.js that means server-side data fetching for the route (server components or the data-fetching functions that run at build or request time) rather than a client-side fetch after mount. The principle is framework-agnostic: anything Google must index belongs in the server-rendered HTML, not in a client-side fetch that runs after load. Non-critical, interactive content can stay client-side, but the content you need ranked must be present before the second-pass render is even required.

Hydration mismatch and JavaScript navigation

Hydration mismatch happens when the server-rendered HTML does not match what the client renders on top of it. Beyond breaking the user experience, a significant mismatch creates ambiguity about which version Google indexes, since the rendered DOM may diverge from the delivered HTML. Keep server and client output consistent so the indexed content is the content you intend.

Crawlability also depends on real links. Navigation built on click handlers that change state without a true href may not be followed, because Googlebot follows anchor elements with real URLs, not arbitrary JavaScript click behavior. Use proper anchor links (the framework’s link component renders these) for anything you want crawled. And watch Core Web Vitals: heavy JavaScript bundles inflate Largest Contentful Paint and degrade Interaction to Next Paint, which replaced First Input Delay in March 2024. The current field targets are LCP at or under 2.5 seconds, INP at or under 200 milliseconds, and CLS at or under 0.1 at the 75th percentile. Oversized bundles work against all three.

The migration confound

When rankings drop after moving to a JavaScript framework, the cause is usually not the framework. It is far more often broken or missing redirects and changed URL structures introduced during the migration. Old URLs that 404 or fail to redirect to their new equivalents lose their accumulated equity, and the timing makes the framework look guilty. Before blaming rendering, audit redirect coverage: confirm every old URL resolves to its new counterpart with a proper redirect, and that internal links point to live URLs rather than chaining through redirects. Diagnose the redirect map first, then investigate rendering.

One thing to avoid: do not reach for dynamic rendering as the primary solution. Google has deprecated dynamic rendering and recommends server-side rendering or static generation instead. Solve the rendering problem at the source by serving HTML, not by maintaining a separate bot-specific render path.

Frequently Asked Questions

Does using React or Vue hurt my SEO?

Not inherently. These frameworks rank fine when content is server-rendered or statically generated so it appears in the HTML Googlebot reads. The risk comes from client-side rendering, where content depends on a delayed second-pass render that can fail. Choose SSR, SSG, or ISR for content you need indexed.

How do I know if Googlebot can actually see my content?

Use Search Console URL Inspection, run Test Live URL, and view the rendered HTML and screenshot. Confirm the critical content is present and check for JavaScript errors or failed resources. Comparing view-source against the rendered DOM shows exactly what JavaScript added and whether it added what matters.

My rankings dropped after migrating to Next.js. Is it a rendering problem?

Usually not. Ranking drops after a migration are most often broken or missing redirects and changed URLs, not the framework’s rendering. Audit redirect coverage first to confirm every old URL resolves to its new equivalent, then investigate rendering if content is genuinely missing from the rendered HTML.

Sources