Picovert

Next.js App Router SEO: Complete Optimization Guide

2026-04-258 min read

Next.js App Router ships a first-class Metadata API that replaces the old next/head pattern. In 2026, a well-configured Next.js app can achieve a technical SEO score of 100 without any additional plugins. This guide covers every metadata lever available, with copy-paste code examples.

The Metadata API

Every page.tsx and layout.tsx can export a metadata object or a generateMetadata() function:

// Static metadata export const metadata = { title: 'My Page', description: '...' };

// Dynamic metadata export async function generateMetadata({ params }) { const post = await getPost(params.slug); return { title: post.title, description: post.excerpt }; }

Next.js merges metadata from layouts and pages, with page metadata taking precedence. The root layout.tsx is the right place for site-wide defaults.

Title templates

Use title.template in the root layout to automatically append the site name:

export const metadata = { title: { default: 'Picovert', template: '%s | Picovert' } };

Child pages then only need to set title: 'Convert PNG to WebP' and it renders as Convert PNG to WebP | Picovert.

Open Graph and Twitter Card

Social previews require Open Graph meta tags. Next.js generates them from the openGraph and twitter fields:

openGraph: { title, description, url, siteName: 'Picovert', images: [{ url: '/og/page.png', width: 1200, height: 630 }], type: 'website' }, twitter: { card: 'summary_large_image', title, description, images: ['/og/page.png'] }

OG images should be exactly 1200×630 pixels. Serve them as WebP from /public/og/ for the best load time in link previews.

hreflang for multilingual sites

If your site supports multiple languages, hreflang tags tell Google which URL to show for each locale. In Next.js, use the alternates field:

alternates: { canonical: 'https://picovert.com/blog/slug', languages: { 'x-default': 'https://picovert.com/blog/slug', 'en': 'https://picovert.com/blog/slug', 'ko': 'https://picovert.com/ko/blog/slug', 'ja': 'https://picovert.com/ja/blog/slug' } }

Always include x-default pointing to your default language URL.

JSON-LD structured data

Structured data enables rich results (star ratings, FAQs, breadcrumbs) in Google Search. Add a <script type='application/ld+json'> tag in your page's <head>. In Next.js, inject it via the generateMetadata() return or directly in the JSX:

const jsonLd = { '@context': 'https://schema.org', '@type': 'BlogPosting', headline: post.title, datePublished: post.date, author: { '@type': 'Organization', name: 'Picovert' } };

Use Google's Rich Results Test to verify your structured data is parsed correctly.

Sitemaps

Next.js generates XML sitemaps from a sitemap.ts file in the app directory:

export default function sitemap() { return posts.map(post => ({ url: `https://picovert.com/blog/${post.slug}`, lastModified: post.date, changeFrequency: 'monthly', priority: 0.8 })); }

Submit the sitemap URL in Google Search Console after deployment.

robots.txt

Create a robots.ts file in the app directory:

export default function robots() { return { rules: { userAgent: '*', allow: '/', disallow: '/api/' }, sitemap: 'https://picovert.com/sitemap.xml' }; }

Canonical URLs

Always set a canonical URL to prevent duplicate content penalties from paginated pages, URL parameters, or locale variants that shouldn't be indexed separately:

alternates: { canonical: 'https://picovert.com/blog/my-post' }

Core Web Vitals and SEO

Beyond metadata, Google uses LCP, INP, and CLS as ranking signals. A perfect metadata setup paired with poor Core Web Vitals scores will still hurt your rankings. Optimize images (convert to WebP, add width/height, preload LCP), minimize JavaScript, and eliminate layout shifts — the full checklist is in the companion performance post.

Checklist

  • Unique title and description on every page
  • Title under 60 characters, description under 155 characters
  • Open Graph image at 1200×630
  • hreflang for all language variants
  • JSON-LD structured data for blog posts, products, FAQs
  • Sitemap submitted to Search Console
  • Canonical URL on every indexable page
  • robots.txt allowing crawlers, disallowing private paths
  • LCP under 2.5 s, INP under 200 ms, CLS under 0.1