Most
SEO posts rehash the same basics. This one is for developers who already know the basics and want practical Next.js-specific tactics that you can apply today to get better indexing, prettier link previews, and faster real-user experience.
1) Use the Metadata API (app router) — per page, correctly
If you use the app router, export metadata or generateMetadata from the page/layout. It’s the cleanest way to keep titles, descriptions, canonical links, open graph and favicons consistent per route. When metadata is rendered server side, crawlers and social previews see the right content immediately.
Example (Static):
// app/blog/[slug]/page.js
export const metadata = {
title: 'How to optimize a blog post',
description: 'Practical SEO tips for Next.js blog posts',
alternates: { canonical: 'https://yourdomain.com/blog/my-post' },
openGraph: {
title: 'How to optimize a blog post',
description: 'Practical SEO tips for Next.js',
images: [{ url: '/og-images/blog-my-post.png', width: 1200, height: 630 }],
},
}
Example (Dynamic):
// app/blog/[slug]/page.js
export async function generateMetadata({ params }) {
const post = await getPost(params.slug)
return {
title: post.title,
description: post.excerpt,
alternates: { canonical: `https://yourdomain.com/blog/${params.slug}` },
}
}
2) Own your sitemap and robots with the app router
Sitemaps still matter. Next.js has first-class helpers for sitemaps in the app router so you can generate sitemaps at build time or split them for very large sites. Keep your sitemap up to date and submit it to Search Console after major updates.
Tiny example (app router):
// app/sitemap.ts
import { MetadataRoute } from 'next'
import { getAllSlugs } from './lib/cms'
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const slugs = await getAllSlugs()
return slugs.map(s => ({
url: `https://yourdomain.com/blog/${s}`,
lastModified: new Date().toISOString(),
}))
}
Then submit https://yourdomain.com/sitemap.xml to Google Search Console, or list it in robots.txt.
3) Server-render critical content, avoid client-only for SEO text
Google processes JavaScript in stages: crawl, render, index. If your primary content is rendered only on the client, indexing may be delayed or incomplete. Render key content server side (SSG/SSR/ISR) so crawlers immediately see it. Dynamic rendering exists as a fallback but is not recommended long term.
Quick guideline:
- Marketing pages and blog posts: SSG or ISR.
- Frequently changing lists (search results): SSR or carefully designed hybrid.
- Personalised dashboard UI: client-side is fine (not for SEO).
4) Make images SEO-friendly using next/image
Images affect page speed and visual stability. Use next/image for automatic resizing, modern formats and to avoid layout shift. Always include good alt text and a sensible sizes / priority policy for hero images.
Example:
import Image from 'next/image'
export default function Hero({ post }) {
return (
<Image
src={post.hero}
alt={post.title}
width={1200}
height={630}
priority
sizes="(max-width: 768px) 100vw, 1200px"
/>
)
}
5) Use JSON-LD structured data server side for rich results
JSON-LD still makes it easier for Google to understand your content and can unlock rich snippets like article cards, FAQs, product info and more. Inject JSON-LD from the server so crawlers see it immediately, then validate with Google’s Rich Results Test. Google recommends JSON-LD.
Example (server component):
const jsonLd = {
"@context": "https://schema.org",
"@type": "Article",
"headline": post.title,
"datePublished": post.publishedAt,
"author": { "@type": "Person", "name": post.author },
"image": post.heroFullUrl,
"publisher": {
"@type": "Organization",
"name": "Your Site",
"logo": { "@type": "ImageObject", "url": "https://yourdomain.com/logo.png" }
}
}
export default function HeadTags() {
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
</>
)
}
Out-of-the-box ideas worth trying
- Serve simplified content to crawlers with server components — render a minimal SEO-first layer server-side and hydrate interactive bits later.
- Split large sitemaps dynamically — for very large sites, break sitemaps by date or category so Search Console processes them faster. Next.js supports splitting sitemaps.
- Next.js
- Generate OG images at build time or on demand using Next.js image APIs so every post has a unique share image without manual work. (Metadata API can help here.)
- Next.js
- Use revalidate tags to invalidate groups of pages instead of single paths when your CMS updates a taxonomy. This keeps content consistent without triggering thousands of rebuilds.
Top comments (0)