JavaScript Framework SEO

SEO for JavaScript frameworks — React, Next.js, Vue, Nuxt, Angular, Svelte, and Astro. How rendering mode (SSR, SSG, CSR) determines what Google can index, and which frameworks handle SEO best out of the box.

First published: Jun 26, 2026 · Last updated: Jul 19, 2026 · Advanced
demand #21 in Platform SEO#132 in Technical SEO#181 on the site

JavaScript framework SEO comes down to rendering mode: SSG and SSR give Googlebot pre-built HTML; CSR requires JavaScript execution that may or may not succeed. Next.js and Nuxt have the most built-in SEO support (SSR, SSG, ISR, metadata APIs). Pure React and Vue in CSR mode are the riskiest for SEO. Astro's island architecture is excellent for SEO by default. Angular requires SSR via @angular/ssr (formerly Angular Universal) for reliable indexation.

TL;DR — At the technical level, JS framework SEO is about: (1) renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. architecture for the initial HTML payload, (2) metadata injection into <head> before the response is sent, (3) how the framework handles hydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers. and link discovery, and (4) ISR cache staleness. Next.js has the most complete built-in SEO tooling (Metadata API, built-in app/sitemap.ts, image optimization). Astro is the most SEO-safe by architecture.

Rendering mode, metadata API, and ISR support by framework

Framework capabilities and defaults change by version; verify them in each framework’s official documentation. Evidence for this claim Primary standard or official documentation supporting the adjacent article claim. Scope: Protocol semantics and Search behavior are kept separate; no indexing, ranking, or migration-timing guarantee is inferred. Confidence: high · Verified: web.dev: Rendering on the Web No renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. mode guarantees indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed. or rankings. Evidence for this claim Primary standard or official documentation supporting the adjacent article claim. Scope: Protocol semantics and Search behavior are kept separate; no indexing, ranking, or migration-timing guarantee is inferred. Confidence: high · Verified: Google: JavaScript SEO basics When you’re comparing frameworks for a decision, test the same routes, content, deployment target and tooling for each candidate — a benchmark that swaps any of those isn’t measuring the framework, it’s measuring the difference in setup.

FrameworkDefault renderingBuilt-in metadata APISSR/SSG supportEcosystem SEO support
AstroSSG (islands)Yes (<head> in .astro files, Content Collections for structured dataStructured data is a standardized way of labeling page content (using the schema.org vocabulary in JSON-LD, Microdata, or RDFa) so search engines can understand its meaning. It's not a direct ranking factor — its value is rich results and entity understanding.)BothStrong
Next.jsSSR/SSG (configurable)Yes (Metadata API in App Router)Both + ISRExcellent
NuxtSSR by defaultYes (useHead, useSeoMeta)Both + ISRExcellent
SvelteKitSSR by defaultYes (svelte:head)BothGood
React Router (framework mode)SSR by defaultYes (meta export)SSR + DeferredGood
AngularCSR by defaultVia Angular Meta/Title servicesVia @angular/ssrModerate
React (bare)CSRManual (react-helmet-async; react-helmet is unmaintained)Via Next.js/GatsbyDepends on wrapper
Vue (bare)CSRManual (@unhead/vue; vue-meta is unmaintained)Via NuxtDepends on wrapper

Remix v2 and React Router v7 merged — Remix’s server rendering and data-loading model now ships as React Router’s “framework mode,” and that’s the stable upgrade path for Remix v2 apps. Remix 3 is a separate, experimental React-less rewrite (beta), not the successor to Remix v2 — don’t treat it as a drop-in SSR option for a React codebase.

Metadata injection timing — Metadata (<title>, <meta>) must be in the server response, not added by JavaScript after page load. Next.js’s Metadata API, Nuxt’s useSeoMeta, and Astro’s <head> component handle this correctly. document.title = '...' or React Helmet in CSR mode does not — it runs after the initial HTML is served.

Link discoveryGooglebotGooglebot is Google's web crawler — the software that fetches pages so Google can index and rank them. It comes in two variants, Googlebot Smartphone (primary, under mobile-first indexing) and Googlebot Desktop, and runs an evergreen Chromium renderer. discovers links by parsing HTML. Links added via JavaScript (onClick, dynamic routing without <a> tags) may not be discovered. Use real <a href> elements for important navigation.

Hydration and duplicate contentThe same or very similar primary content reachable at more than one URL. There's no general duplicate content penalty — the real costs are possible signal dilution, the wrong URL getting chosen, and less-efficient crawling. — If SSR and CSR render different content (hydration mismatch), you may end up with indexed content that doesn’t match what users see. Test for hydration errors in the browser console.

Soft 404sA soft 404 is a URL that returns a success status code (usually 200 OK) even though the page is empty, missing, or shows a 'not found' message. It isn't a status code a server sends — it's a label search engines apply after comparing the response code against the rendered content, and they treat the page like a 404 for indexing. — Client-side routers can silently render a “page not found404 Not Found is the HTTP client-error status code a server returns when it can't find the requested URL — RFC 9110 defines it as no current representation, or unwillingness to disclose one. A \"hard 404\" actually returns the 404 status; a \"soft 404\" returns a success code (like 200) for a page that's really gone. 404s are normal and expected: the fact that some URLs 404 doesn't affect your site's other, successful pages, and Google de-indexes 404'd URLs over time (probably retrying for some period, less and less often).” UI while returning a 200 HTTP status. Search engines index these as real pages. Make sure your 404 page returns a real 404 status, and server-side redirectsA redirect sends browsers and crawlers from a requested URL to a different one. An HTTP redirect specifically is a 3xx status code paired with a Location header; meta refresh and JavaScript redirects achieve a similar navigation without being a 3xx response themselves. Permanent redirects (301/308) are Google's signal the target should be canonical; temporary ones (302/303/307) aren't. return 301.

ISR cache invalidation — In Next.js and Nuxt ISR setups, stale pages can be served to crawlersA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. during the revalidation window. Set a time-based revalidate interval, and for content that changes on a schedule you don’t control (a CMSA content management system (CMS) is software that lets users create, manage, and publish digital content — like blog posts and pages — without writing raw code. WordPress, Drupal, and Joomla are the most common open-source CMS platforms. save, for example), pair it with on-demand revalidation triggered from a webhook-backed route handler — see the Next.js ISR guide for the full API.

// app/blog/[id]/page.tsx — time-based revalidation
export const revalidate = 3600 // re-check this page at most once an hour

// app/api/revalidate/route.ts — on-demand revalidation, called by a CMS webhook
import { revalidatePath } from 'next/cache'
import { NextRequest, NextResponse } from 'next/server'

export async function POST(request: NextRequest) {
  const { path, secret } = await request.json()
  if (secret !== process.env.REVALIDATE_SECRET) {
    return NextResponse.json({ message: 'Invalid secret' }, { status: 401 })
  }
  revalidatePath(path) // e.g. '/blog/1' — next request regenerates fresh HTML
  return NextResponse.json({ revalidated: true })
}

Add an expert note

Pin an expert quote

New person? Create their unclaimed profile at /admin/experts/ → Pin a quote first.