How to Use React JS for SEO with Next.js: Server-Side Rendering and Metadata Control
Next.js provides a full-stack rendering pipeline that sends fully rendered HTML—including all meta tags—to search engines before JavaScript executes, making React JS for SEO achievable through server-side rendering, static generation, and the generateMetadata API.
Using React JS for SEO has historically challenged developers because client-side rendering hides content from search crawlers. Next.js, the React framework maintained by Vercel, solves this by giving you explicit control over the HTML document through server-side rendering and metadata management.
Server-Side Rendering and Static Generation
The foundation of using React JS for SEO in Next.js lies in choosing the right rendering mode. When a page is rendered on the server or at build time, the complete HTML—including all <head> tags—is sent to the client before any JavaScript runs. Search bots index this HTML directly, making your metadata immediately discoverable.
Next.js supports three rendering strategies:
- Static Generation: HTML is generated at build time or on-demand using
generateStaticParamsandrevalidate. The full HTML is cached and instantly served to crawlers. - Server-Side Rendering: HTML is generated on each request using
generateMetadataorgetServerSideProps. This guarantees fresh metadata for dynamic titles and Open Graph tags. - Client-Side Rendering: Content appears only after hydration. SEO works only if bots execute JavaScript, which is generally unreliable.
The core rendering pipeline lives in packages/next/src/server/app-render/app-render.tsx. The framework creates a rendering span and calls your generateMetadata function before streaming HTML, ensuring metadata is "properly parented" in the initial payload【/tmp/instagit_geyfbtik/packages/next/src/server/app-render/app-render.tsx#L2676-L2677】.
Managing Metadata with Head and generateMetadata
The Head Component (Pages Router)
For the Pages Router, Next.js provides the Head component from next/head. This component collects all children and injects them into the document's <head> on the server. The implementation in packages/next/src/shared/lib/head.tsx validates usage and warns in development if you add disallowed elements like <script> or stylesheet <link> tags【/tmp/instagit_geyfbtik/packages/next/src/shared/lib/head.tsx#L30-L40】.
import Head from 'next/head'
export default function BlogPost({ title, description }) {
return (
<>
<Head>
<title>{title}</title>
<meta name="description" content={description} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
</Head>
<article>{/* content */}</article>
</>
)
}
The generateMetadata API (App Router)
Next.js 13+ introduces the generateMetadata API for the App Router. Export an async generateMetadata function from a page or layout to compute titles, descriptions, Open Graph tags, and canonical URLs on the server before the HTML stream starts.
// app/posts/[slug]/page.tsx
import type { Metadata } from 'next'
export async function generateMetadata({ params }: { params: { slug: string } }): Promise<Metadata> {
const post = await fetchPost(params.slug)
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
url: `https://example.com/posts/${params.slug}`,
},
alternates: {
canonical: `https://example.com/posts/${params.slug}`,
},
}
}
Critical SEO Pitfalls to Avoid
Document Title Restrictions
Never place a <title> tag directly in your custom _document.tsx file. Next.js emits a development warning if you do, because titles belong in page-level <Head> components or generateMetadata. The check is implemented in packages/next/src/pages/_document.tsx around lines 620-628【/tmp/instagit_geyfbtik/packages/next/src/pages/_document.tsx#L620-L628】.
Invalid Head Children
The Head component rejects <script> tags and stylesheet <link> tags. If you include these, Next.js warns you during development in packages/next/src/shared/lib/head.tsx【/tmp/instagit_geyfbtik/packages/next/src/shared/lib/head.tsx#L30-L40】. Use next/script for external scripts and import CSS directly or use _document for global stylesheets.
Reliance on Client-Side Rendering
Client-only rendering delays content until after JavaScript executes. While Googlebot can execute JavaScript, many other search engines and social media crawlers do not run JavaScript reliably. Always use Static Generation or Server-Side Rendering for SEO-critical pages to ensure metadata appears in the initial HTML payload.
Summary
- Next.js makes React JS for SEO achievable by rendering HTML on the server before sending it to crawlers.
- Use Static Generation for immutable content and Server-Side Rendering for dynamic data, ensuring metadata is included in the initial payload.
- Manage metadata with
next/headin the Pages Router orgenerateMetadatain the App Router, both of which execute on the server. - Avoid placing
<title>in_document.tsxand never put<script>tags insidenext/head. - Reference the source implementation in
packages/next/src/server/app-render/app-render.tsx,packages/next/src/shared/lib/head.tsx, andpackages/next/src/pages/_document.tsxto understand how Next.js enforces these SEO patterns.
Frequently Asked Questions
Is React JS good for SEO?
React JS by itself relies on client-side rendering, which can hide content from search crawlers that do not execute JavaScript. However, when used with Next.js, React becomes fully SEO-capable because Next.js pre-renders HTML on the server, ensuring all content and metadata are visible to crawlers immediately.
What is the difference between Head and generateMetadata?
The Head component from next/head is used in the Pages Router to inject tags into the document head through JSX composition. The generateMetadata API is an async function exported from App Router pages that runs on the server before streaming begins, allowing you to compute metadata from databases or APIs and return a structured metadata object.
How does Next.js handle metadata for dynamic routes?
For dynamic routes in the App Router, you export an async generateMetadata function that receives the params object containing route segments. This function executes on the server during the rendering span defined in packages/next/src/server/app-render/app-render.tsx, allowing you to fetch data specific to the slug or ID and return unique titles, descriptions, and Open Graph tags for each URL.
Can I use client-side rendering for SEO-critical pages?
You should not rely on client-side rendering for pages where SEO is critical. While Googlebot can execute JavaScript, many other search engines and social media crawlers do not run JavaScript reliably. Next.js provides Static Generation and Server-Side Rendering specifically to ensure that SEO-critical metadata appears in the initial HTML payload without requiring client-side hydration.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →