SEO determines whether your application gets discovered or buried in search results. Shipping a React app feels like you have done everything right until you check your traffic and see almost nothing happening. Google crawlers hit what looks like an empty shell, bounce quickly, and your rankings never show up. While modern crawlers have improved at rendering JS, it is still a well-documented issue that Googlebot often needs multiple passes to properly index client-side rendered (CSR) React applications (though there are ways to make React render over SSR), leading to delayed visibility.
And if you are aiming to rank in LLM-driven search or AI aggregators, bots like GPTBot and ClaudeBot often do not render JS (as rendering JS is a heavy task that requires significantly more resources). When they crawl your site, they just see a blank page, which can seriously hurt your AI discoverability.
SSR (Server-Side Rendering), static site generation, and incremental static generation are considered great for SEO as they render content on the server and send fully formed HTML to the client which search engines can index immediately—unlike client-side rendering where content depends on JS execution. This is something Next.js makes easy to implement.
Explaining Rendering Strategies in Short
1. Server-Side Rendering (SSR)
Content is rendered on the server and sent as ready-to-use HTML to the browser, which improves initial load speed and makes it easier for search engines to index pages—something commonly used in frameworks like Next.js.
2. Static Site Generation (SSG)
Pages are pre-built at build time and served as static HTML, which makes them extremely fast and easy for search engines to index. It is ideal for content that does not change often, like blogs or docs, and is widely supported in frameworks like Next.js.
3. Incremental Static Regeneration (ISR)
Static pages are generated at build time but can be updated later without rebuilding the entire site. This allows you to serve fast static content while still keeping data fresh by regenerating pages in the background—something supported out-of-the-box in Next.js.
You statically pre-render pages but allow them to revalidate in the background:
export const revalidate = 60; // seconds4. Client-Side Rendering (CSR)
The browser loads a minimal HTML shell and then uses JS to render content on the client side, which can lead to slower initial load and SEO challenges since content depends on JS execution, unlike server-rendered approaches.
Next.js App Router SEO File Structure
In Next.js App Router, you can define special metadata files (either static or dynamic) and Next.js automatically serves them and updates SEO-related head tags.
app/
├── layout.tsx // root layout (global metadata wrapper)
├── page.tsx // homepage (most important SEO page)
├── robots.ts // controls crawler access
├── sitemap.ts // helps search engines discover pages
├── products/ // main SEO section
│ ├── page.tsx // listing page (indexable)
│ └── [slug]/
│ └── page.tsx // dynamic pages (high SEO value)What's nice about Next.js is that it takes care of this automatically. These special files are served at the correct URLs without any extra setup from your side.
Setting Up Metadata (app/layout.tsx)
Metadata configuration is the base of SEO. It defines your page title, description, and how your site appears in search and social previews. In Next.js, you can set it in layout or page files, and it is automatically injected into the HTML head tag.
Here is how:
import type { Metadata } from "next";
export const metadata: Metadata = {
metadataBase: new URL("https://deeppanchal.in"),
title: {
default: "Deep Panchal",
template: "%s | Deep Panchal",
},
description:
"Portfolio of Deep Panchal showcasing modern web applications and full stack development work",
keywords: [
"Deep Panchal",
"Full stack developer",
"Next.js developer",
"React developer",
"Portfolio",
],
authors: [
{
name: "Deep Panchal",
url: "https://deeppanchal.in",
},
],
openGraph: {
title: "Deep Panchal",
description:
"Modern web applications and full stack projects by Deep Panchal",
url: "https://deeppanchal.in",
siteName: "Deep Panchal",
images: ["/og.png"],
type: "website",
},
robots: {
index: true,
follow: true,
},
alternates: {
canonical: "/",
},
};What Each Field Does
- metadataBase ensures all relative links like images and canonicals are converted into full URLs so they work correctly everywhere.
- title template lets you reuse a pattern like
%s | Deep Panchalso each page gets its own title while keeping consistent branding. - canonical helps search engines understand the primary version of a page and avoids duplicate content confusion.
- openGraph defines how your pages appear when shared on social platforms like Facebook or LinkedIn.
Similarly, you can generate dynamic metadata based on data like slugs or content:
import type { Metadata } from "next";
export async function generateMetadata({ params }): Promise<Metadata> {
const { slug } = params;
// Make an API call here and replace the data
const post = {
title: `Project ${slug}`,
description: `Details about ${slug}`,
};
return {
title: post.title,
description: post.description,
};
}Setting Up Robots.txt (app/robots.ts)
Tells search engine crawlers which parts of your site they are allowed or not allowed to access. It helps control crawling and improves how your site is indexed.
import type { MetadataRoute } from "next";
export default function robots(): MetadataRoute.Robots {
return {
rules: {
userAgent: "*",
allow: "/",
disallow: ["/admin", "/api", "/private"],
},
sitemap: "https://deeppanchal.in/sitemap.xml",
};
}Output:
User-Agent: *
Allow: /
Disallow: /admin
Disallow: /api
Disallow: /private
Sitemap: https://deeppanchal.in/sitemap.xmlSitemap Generation (app/sitemap.ts)
Plays a crucial role in SEO by helping search engines discover all your pages, including orphaned pages that might not be linked anywhere on your site, making sure nothing important gets missed during crawling.
import type { MetadataRoute } from "next";
export default function sitemap(): MetadataRoute.Sitemap {
return [
{
url: "https://deeppanchal.in",
lastModified: new Date(),
changeFrequency: "yearly",
priority: 1,
},
{
url: "https://deeppanchal.in/projects",
lastModified: new Date(),
changeFrequency: "monthly",
priority: 0.9,
},
{
url: "https://deeppanchal.in/about",
lastModified: new Date(),
changeFrequency: "yearly",
priority: 0.7,
},
{
url: "https://deeppanchal.in/contact",
lastModified: new Date(),
changeFrequency: "yearly",
priority: 0.6,
},
];
}What Each Field Means
- url → The exact page URL you want search engines to index.
- lastModified → Tells crawlers when the page was last updated so they know when to recheck it.
- changeFrequency → Gives a hint about how often the content changes, like daily, weekly, or monthly.
- priority → Indicates how important a page is compared to others on your site.
(You can also create dynamic sitemaps by fetching your data and looping over it to populate these fields.)
Implementing Structured Data (JSON-LD)
Structured data is what allows your site to appear with rich results like FAQ dropdowns, ratings, or detailed previews instead of just plain links, which often leads to better visibility and higher click-through rates.
It works by giving search engines more context about your content using a structured format like JSON-LD based on Schema.org.
export default function ArticlePage({ article }) {
const articleJsonLd = {
"@context": "https://schema.org",
"@type": "Article",
headline: article.title,
description: article.excerpt,
image: article.coverImage,
datePublished: article.publishedAt,
author: {
"@type": "Person",
name: article.author,
},
publisher: {
"@type": "Organization",
name: "Deep Panchal",
logo: {
"@type": "ImageObject",
url: "https://deeppanchal.in/icon.png",
},
},
mainEntityOfPage: `https://deeppanchal.in/articles/${article.slug}`,
};
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(articleJsonLd),
}}
/>
{/* Content */}
</>
);
}Understanding Core Web Vitals
How your site feels matters a lot. Speed, responsiveness, and stability all affect rankings, and that's what Core Web Vitals measure.
LCP Optimization (aim under 2.5s)
- Use
priorityon above-the-fold images (users see immediately). - Static generation pre-renders pages for faster load.
- Server Components reduce JS sent to the client.
INP Optimization (aim under 200ms)
- Server Components shift work to the server, reducing client load.
- Streaming with suspense improves perceived responsiveness.
- Avoid heavy computations during user interactions.
CLS Optimization (aim under 0.1)
next/imagereserves space usingwidthandheight.next/fontprevents layout shifts from font loading.- Avoid adding content above already rendered elements.
Image Optimization
import Image from "next/image";
// Main banner (visible on load)
<Image
src="/banner.jpg"
alt="Homepage banner"
width={1200}
height={600}
priority
/>
// Other images (load when scrolled into view)
<Image
src={item.image}
alt={item.title}
width={800}
height={450}
/>Important Image Props (and why they matter)
- priority forces important images to load early instead of waiting. Useful for above-the-fold content like hero sections and improves LCP.
- width and height define the image dimensions beforehand, which prevents layout shifts and keeps your page stable, improving CLS.
- alt provides a description of the image for screen readers and search engines. It also acts as fallback text if the image fails to load. Always include meaningful alt text.
Font Optimization
Use next/font to load fonts in a way that avoids layout shifts and keeps text stable while the page loads.
import { Inter } from "next/font/google";
const inter = Inter({
subsets: ["latin"],
display: "swap",
});
export default function Layout({ children }) {
return (
<html lang="en" className={inter.className}>
<body>{children}</body>
</html>
);
}Conclusion
SEO in Next.js is less about hacks and more about using the right fundamentals. The framework already gives you everything you need, from metadata handling to performance optimization.
By structuring your pages properly, adding meaningful metadata, using sitemaps and robots files, and keeping performance in check, you make it easier for search engines and even AI systems to understand and surface your content.
Learn More
Official Next.js Documentation:
Follow for more :)
Social SEO (Open Graph and Twitter Cards)
Controls how your pages look when shared on platforms like Facebook, LinkedIn, and X. Instead of a plain link, you get previews with a title, description, and images.
For a deeper understanding of how Open Graph and X images work in Next.js, including static and dynamic generation, you can refer to the official documentation:
Next.js Metadata Open Graph Image Docs