BLOG — SEO & DEVELOPMENT
Next.js has excellent SEO capabilities built in. But you need to use them correctly. In this article, we walk through every step — from metadata to structured data.
Metadata is the foundation of SEO in Next.js. The App Router provides a powerful system through the generateMetadata function. Each page can define its own title, description, and Open Graph tags.
import type { Metadata } from 'next'
export async function generateMetadata(): Promise<Metadata> {
return {
title: 'My Page — My Site',
description: 'A short description for search engines.',
openGraph: {
title: 'My Page — My Site',
description: 'A short description.',
type: 'article',
},
}
}Pro tip: use a title template in your root layout so you don't have to repeat ' | My Site' everywhere:
// app/layout.tsx
export const metadata: Metadata = {
title: {
template: '%s | My Site',
default: 'My Site',
},
}Don't forget alternates for multilingual sites. This tells Google which language versions exist:
alternates: {
canonical: 'https://example.com/en/page',
languages: {
nl: 'https://example.com/nl/pagina',
en: 'https://example.com/en/page',
},
}A sitemap tells search engines which pages your site has. Next.js supports this natively via a sitemap.ts file:
// app/sitemap.ts
import type { MetadataRoute } from 'next'
export default function sitemap(): MetadataRoute.Sitemap {
return [
{
url: 'https://example.com',
lastModified: new Date(),
changeFrequency: 'monthly',
priority: 1,
},
]
}For robots.txt you create a similar file:
// app/robots.ts
import type { MetadataRoute } from 'next'
export default function robots(): MetadataRoute.Robots {
return {
rules: {
userAgent: '*',
allow: '/',
},
sitemap: 'https://example.com/sitemap.xml',
}
}Both files are automatically generated as /sitemap.xml and /robots.txt when you build the site.
Structured data helps Google understand your content better and can result in rich snippets — enhanced search results with stars, FAQ dropdowns, or breadcrumbs.
The most common format is JSON-LD. In Next.js, you add this as a script tag:
function FAQSchema({ items }: { items: { q: string; a: string }[] }) {
const schema = {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: items.map((item) => ({
'@type': 'Question',
name: item.q,
acceptedAnswer: {
'@type': 'Answer',
text: item.a,
},
})),
}
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
)
}Other useful schema types: Article (for blog posts), Organization (for your company page), BreadcrumbList (for navigation), and HowTo (for tutorials).
Google uses Core Web Vitals as a ranking factor. The three metrics that matter:
How fast does the largest element on the page load? Target: under 2.5 seconds. Use next/image for automatic image optimization.
How fast does the page respond to interaction? Target: under 200ms. Minimize client-side JavaScript and use Server Components where possible.
Does the layout jump during loading? Target: under 0.1. Always provide width and height for images, and load fonts via next/font.
Next.js helps with each of these: next/image optimizes images automatically, next/font prevents layout shifts for fonts, and Server Components reduce the JavaScript bundle.
import Image from 'next/image'
import { GeistSans } from 'geist/font/sans'
// Optimized image
<Image
src="/hero.jpg"
alt="Hero image"
width={1200}
height={630}
priority
/>
// Font without layout shift
<body className={GeistSans.className}>The Next.js App Router uses Server Components by default. This is a major advantage for SEO for two reasons:
First: Server Components render on the server. Google sees the complete HTML without needing to execute JavaScript. This is more reliable than client-side rendering where Googlebot has to run the JavaScript.
Second: Server Components send no JavaScript to the browser. Less JavaScript = faster page = better Core Web Vitals = higher ranking.
Rule of thumb: make everything a Server Component unless you need interactivity (forms, dropdowns, animations). Only mark those components with 'use client'.
// Server Component (default) — good for SEO
export default async function BlogPost() {
const post = await getPost()
return <article>{post.content}</article>
}
// Client Component — only for interactivity
'use client'
export function ContactForm() {
const [name, setName] = useState('')
return <form>...</form>
}Traditional SPAs that rely entirely on client-side rendering are trickier for SEO — Google has to execute JavaScript to see the content. Next.js solves this with Server Components and SSR: the HTML is rendered on the server, allowing search engines to index the content directly.
Use SSG (Static Site Generation) for content that doesn't change often — blog posts, landing pages, documentation. Use SSR (Server-Side Rendering) for content that varies per request — personalized pages, search results. In Next.js, you can choose per page.
Use Google Search Console for indexing status and search performance. Lighthouse (in Chrome DevTools) measures Core Web Vitals. And Google's Rich Results Test checks your structured data. Always test on a production build (next build + next start), not the dev server.
We help developers and businesses with technical SEO implementation in Next.js. From audit to implementation.