Skip to main content
Development August 11, 2026 10 min read

Next.js Metadata API: Titles, Canonicals & Open Graph Done Right

Next.js Metadata API: Titles, Canonicals & Open Graph Done Right

Next.js App Router replaced scattered Head tags with a first-class Metadata API — but titles, canonicals, and Open Graph still break in production when metadataBase is missing or generateMetadata returns relative URLs. This guide walks through patterns that keep search and social previews correct, and pairs them with ToolMars utilities for slug, snippet, crawl, and schema checks.

Whether you ship a marketing site or a docs product, consistent metadata is the cheapest SEO win: one source of truth for title templates, absolute canonicals, and OG images that actually resolve.

Preview titles and metas before you ship

Free SERP preview — pixel-width checks for desktop and mobile snippets.

Open SERP Preview

Static metadata vs generateMetadata

Fixed routes (pricing, about, tool landing pages) should export a metadata object. Dynamic routes — blog posts, product pages, localized URLs — need generateMetadata so titles and canonicals reflect the actual resource. Mixing them incorrectly is a common source of duplicate titles and self-referencing wrong URLs.

  • Static export — fast, cacheable, ideal for marketing pages.
  • generateMetadata — await CMS/data; return per-slug SEO fields.
  • Root layout templates — use title.template for brand suffixes without repeating them on every page.

Canonicals and generateMetadata example

Set metadataBase in the root layout, then use alternates.canonical on each page. Build clean paths with a slug generator when content editors invent titles — hyphens, lowercase, no stop-word spam.

// app/layout.tsx
export const metadata = {
  metadataBase: new URL("https://www.toolmars.com"),
  title: { default: "ToolMars", template: "%s | ToolMars" },
};

// app/blogs/[slug]/page.tsx
export async function generateMetadata({
  params,
}: {
  params: Promise<{ slug: string }>;
}): Promise<Metadata> {
  const { slug } = await params;
  const post = await getPost(slug);

  return {
    title: post.title,
    description: post.excerpt,
    alternates: {
      canonical: `https://www.toolmars.com/blogs/${slug}/`,
    },
    openGraph: {
      title: post.title,
      description: post.excerpt,
      url: `https://www.toolmars.com/blogs/${slug}/`,
      type: "article",
      images: [{ url: post.image, width: 1200, height: 630 }],
    },
    twitter: {
      card: "summary_large_image",
      title: post.title,
      description: post.excerpt,
      images: [post.image],
    },
  };
}
Canonical pitfall: Do not canonicalize filtered or paginated URLs to themselves if the preferred indexable URL is the clean collection page. Pick one canonical per duplicate cluster and stick to it.

Open Graph and social cards

Social platforms need absolute image URLs (typically 1200×630). Relative paths silently fail when metadataBase is unset. Keep openGraph.url aligned with the canonical. After drafting copy, check truncation in the SERP preview tool — Google and social cards both punish runaway lengths.

Metadata checklist

  1. Define metadataBase once in the root layout.
  2. Use title templates for brand consistency.
  3. Return alternates.canonical for every indexable page.
  4. Provide OG/Twitter images as absolute URLs.
  5. Preview snippet length with the meta generator.
  6. Add JSON-LD via the schema generator for Article or FAQ pages.
  7. Align crawl policy with a robots.txt that allows indexable routes.

Add structured data next to metadata

Generate Article, FAQ, and SoftwareApplication JSON-LD in the browser.

Open Schema Generator

Robots, noindex, and crawl hygiene

Use metadata.robots for page-level noindex (thank-you pages, internal search). Keep site-wide Disallow rules in robots.txt — conflicting signals confuse crawlers. When you retire URLs, pair Metadata changes with redirects and a clean crawl map.

Related Tools on ToolMars

Conclusion

The Metadata API is powerful when you treat it as a contract: absolute URLs, explicit canonicals, and previews before merge. Wire generateMetadata to real content, validate snippets and schema with ToolMars, and your Next.js pages will speak clearly to both Google and social platforms.

Preview titles and metas before you ship

Free SERP preview — pixel-width checks for desktop and mobile snippets.

Preview My Title & Meta

Frequently Asked Questions

Should I use metadata export or generateMetadata?

Use a static metadata export for fixed pages. Use async generateMetadata when title, description, or canonical depend on params, searchParams, or fetched CMS data.

How do I set a canonical URL in the Next.js Metadata API?

Set alternates.canonical to the absolute preferred URL (or a path Next.js can resolve against metadataBase). Avoid pointing canonicals at query-string variants you do not want indexed.

What is metadataBase and why does it matter?

metadataBase is the origin Next.js uses to resolve relative Open Graph images, canonicals, and other URLs into absolute https links required by crawlers and social platforms.

Do Open Graph tags replace title and meta description?

No. og:title and og:description power social cards; title and description still matter for search snippets. Keep them consistent unless you intentionally customize social copy.

Can Next.js Metadata API emit JSON-LD?

Structured data is usually injected via a script tag in the page (dangerouslySetInnerHTML) or a dedicated component — not through the metadata title/description fields alone.

How long should Next.js page titles be?

Aim for roughly 50–60 characters so Google does not truncate. Preview pixel width with a SERP tool before merging — wide characters consume space faster.

Where should robots rules live relative to Metadata API?

Page-level robots (index/noindex) can go in metadata.robots. Site-wide crawl rules belong in robots.txt — generate both and keep them consistent.

Written by Toolmars Labs Team