What “good metadata” means
A good metadata setup should do 5 things:
- Unique title per page, following a consistent pattern.
- Meaningful description that matches the page content.
- Correct canonical URL (absolute, preferred host/protocol).
- Good social previews (Open Graph + Twitter card).
- Safe indexing rules (no accidental indexing for staging).
Set solid defaults in app/layout
Use defaults so every route gets “good enough” metadata, then override per page.
In App Router, define a metadata export in app/layout.tsx.
Example default metadata (App Router)
// app/layout.tsx
import type { Metadata } from "next";
const siteUrl = "https://blog.olamisan.com";
export const metadata: Metadata = {
metadataBase: new URL(siteUrl),
title: {
default: "Olamisan — Practical Developer Notes",
template: "%s — Olamisan"
},
description:
"Practical notes and tutorials about web performance, technical SEO, automation, and developer tools.",
alternates: {
canonical: "/"
},
openGraph: {
type: "website",
url: siteUrl,
siteName: "Olamisan",
title: "Olamisan — Practical Developer Notes",
description:
"Practical notes and tutorials about web performance, technical SEO, automation, and developer tools.",
images: [
{
url: "/assets/og-cover.png",
width: 1200,
height: 630,
alt: "Olamisan blog cover"
}
]
},
twitter: {
card: "summary_large_image",
title: "Olamisan — Practical Developer Notes",
description:
"Practical notes and tutorials about web performance, technical SEO, automation, and developer tools.",
images: ["/assets/og-cover.png"]
}
};
Titles: patterns that scale
Titles should be descriptive, human, and consistent. A scalable pattern:
- Post pages: “Exact Topic + Outcome (Optional) — Brand”
- Category pages: “Category Name: What you’ll learn — Brand”
- Homepage: “Brand — Primary topics”
Good title examples
- “Core Web Vitals Quick Wins: LCP, CLS, and INP Improvements — Olamisan”
- “Technical SEO Basics for Developers: What to Fix First — Olamisan”
- “Next.js Metadata: Titles, OG, and Canonicals — Olamisan”
Descriptions: consistent and human
Meta descriptions don’t directly change rankings, but they strongly influence click-through. A simple formula: what it is + who it’s for + what you’ll get.
- Keep it close to what the page actually delivers.
- Avoid keyword stuffing.
- Use consistent voice across pages.
Open Graph and Twitter cards
Open Graph tags improve previews on social platforms and messengers. Even though it’s not a direct ranking factor, it helps your content get clicked and shared.
Minimum social preview setup
- One consistent OG image style (1200×630 recommended).
- Clear title and description that match the page.
- Use absolute URLs (or set
metadataBase).
Canonicals: stop duplicate URL problems
Canonicals tell search engines which URL is the “main” one when multiple URLs show the same content. Common duplicate sources: trailing slash vs no slash, query parameters, and http vs https.
Canonical best practices
- Always use absolute canonical URLs (or ensure they resolve to absolute via
metadataBase). - Canonical should match your preferred host/protocol and trailing slash rule.
- Don’t canonical everything to the homepage.
Example canonical for a post
// app/posts/[slug]/page.tsx (inside metadata generator)
alternates: { canonical: `/posts/${slug}` }
Robots meta: index, noindex, and staging safety
Use robots meta to control indexing behavior. Production pages you want to rank should be indexable. Staging should be blocked and/or noindexed.
Practical rules
- Production: index, follow (default).
- Staging: noindex + block crawling via robots.txt (and ideally protect with auth).
- Thin pages: consider noindex (e.g., internal utility pages).
Dynamic metadata for blog posts (App Router)
For blog posts, generate metadata per slug. This ensures each post has unique title, description, OG tags, and canonical.
Example generateMetadata
// app/posts/[slug]/page.tsx
import type { Metadata } from "next";
// Example: replace with your data source
async function getPost(slug: string) {
return {
title: "Next.js Metadata: Titles, Descriptions, OG, and Canonicals",
description:
"Learn Next.js metadata best practices: titles, descriptions, Open Graph, Twitter cards, and canonical URLs—plus per-route patterns.",
ogImage: "/assets/og-cover.png"
};
}
export async function generateMetadata(
{ params }: { params: { slug: string } }
): Promise<Metadata> {
const post = await getPost(params.slug);
return {
title: post.title,
description: post.description,
alternates: {
canonical: `/posts/${params.slug}`
},
openGraph: {
type: "article",
title: post.title,
description: post.description,
url: `https://blog.olamisan.com/posts/${params.slug}`,
images: [{ url: post.ogImage, width: 1200, height: 630, alt: post.title }]
},
twitter: {
card: "summary_large_image",
title: post.title,
description: post.description,
images: [post.ogImage]
}
};
}
Before-you-ship checklist
- ✅ Unique title for each page
- ✅ Helpful description that matches the page
- ✅ Correct canonical (preferred host/protocol/slash rule)
- ✅ Open Graph + Twitter preview looks correct
- ✅ Staging has noindex + robots blocking + auth (recommended)
- ✅ No accidental noindex shipped to production
FAQ
How do I set a canonical URL in Next.js App Router?
Use the metadata API and set alternates.canonical. Keep it consistent with your preferred URL format.
Should every page have a unique title and description?
Yes. Use a consistent template, but customize the page-specific part so pages don’t look duplicated.
Do Open Graph tags matter for SEO?
Not directly for ranking, but they improve how your content appears when shared, increasing clicks.
How do I prevent staging sites from being indexed?
Add auth, use noindex on staging, and block crawling via robots.txt for the staging domain.