aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Web Development

Next.js SEO: Title, OG and Canonical with Metadata API

When people talk about Next.js SEO, the first thing that comes to mind is filling the page's <head> correctly: a meaningful title, a descriptive description, Open Graph tags for social sharing, and a canonical link that prevents duplicate content. The Metadata API that ships with the App Router lets you do all of this without hand-writing <head> elements, in a type-safe way that resolves on the server. In this guide I walk through a practical setup, from the static metadata object to the dynamic generateMetadata function.

What is the Metadata API?

In the App Router (Next.js 13.2 and later), every layout.tsx and page.tsx file can export metadata in one of two ways: a metadata object for constant values, or an async generateMetadata function for values that depend on the request or on data. Next.js reads these and generates the page's <head> for you. One important rule: these exports only work in Server Component files; you cannot export metadata from a file marked with "use client".

Title and description with static metadata

The simplest form is exporting a constant object. You usually put a site-wide one in the root app/layout.tsx, then add page-specific ones per route.

// app/layout.tsx
import type { Metadata } from "next";

export const metadata: Metadata = {
  title: {
    default: "aslain.dev — Web & Game Development",
    template: "%s | aslain.dev",
  },
  description: "Guides on web, game servers and Discord bot development.",
};

The title.template field is a powerful tool: child pages provide only their own title, and Next.js drops it into %s and appends the site name. For example, if a page sets title: "Contact", the output becomes Contact | aslain.dev. You stop repeating your brand name in every title by hand.

Dynamic Next.js SEO with generateMetadata

On routes whose content comes from a database — blog posts, product pages — the title and description need to be dynamic too. This is where generateMetadata comes in. It receives the same params argument as the page itself, fetches data, and returns metadata.

// app/blog/[slug]/page.tsx
import type { Metadata } from "next";

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,
  };
}

In Next.js 15, params became a Promise, so you have to resolve it with await. An important performance note: fetch calls made inside generateMetadata are deduplicated by Next.js request memoization when repeated in the same page component, so don't worry about fetching the same data twice.

Open Graph and Twitter cards

How your link looks when shared on WhatsApp, X or LinkedIn is controlled by Open Graph tags. The Metadata API exposes these cleanly under the openGraph and twitter keys.

export const metadata: Metadata = {
  openGraph: {
    title: "Next.js SEO Guide",
    description: "Title, OG and canonical with the Metadata API.",
    url: "https://aslain.dev/blog/nextjs-seo",
    siteName: "aslain.dev",
    images: [{ url: "/og/nextjs-seo.png", width: 1200, height: 630 }],
    locale: "en_US",
    type: "article",
  },
  twitter: {
    card: "summary_large_image",
    title: "Next.js SEO Guide",
    images: ["/og/nextjs-seo.png"],
  },
};
  • Image size: 1200×630 pixels is the standard ratio social platforms expect.
  • type: "article": the correct OG type for blog posts; use "website" for the homepage.
  • If you don't specify the Twitter card separately, most fields are inherited from Open Graph; still, it's good practice to set the card type explicitly.

Canonical and language alternates

If the same content is reachable from multiple URLs (a trailing slash, tracking parameters), a canonical tag is essential to tell search engines the authoritative address. The Metadata API provides it under alternates.

export const metadata: Metadata = {
  metadataBase: new URL("https://aslain.dev"),
  alternates: {
    canonical: "/blog/nextjs-seo",
    languages: {
      "en-US": "/en/blog/nextjs-seo",
      "tr-TR": "/tr/blog/nextjs-seo",
    },
  },
};

Always define metadataBase: relative paths (like /blog/... or /og/...) are resolved into absolute URLs against this base. If you omit it, Next.js warns during development and OG images may not appear on some platforms because they aren't absolute URLs. The alternates you list under languages turn into hreflang tags, strengthening multilingual SEO.

Common mistakes

  • Exporting from a client component: exporting metadata from a "use client" file silently does nothing; keep metadata in a Server Component.
  • Forgetting metadataBase: OG images and canonical break because they stay relative.
  • Defining the title twice: title and openGraph.title can differ; if you don't keep them consistent, shares look confusing.
  • Mixing in a manual <head>: in the App Router, don't hand-write a <head> element — the Metadata API should be the single source of truth.

Frequently Asked Questions

Should I use the metadata object or generateMetadata?

If the values are constant (static pages, the root layout), the metadata object is simpler and faster. If the title or description comes from data, params or the request, use generateMetadata. Don't export both from the same file; Next.js does not allow it.

Does the Metadata API also generate sitemap and robots files?

Yes. The app/sitemap.ts and app/robots.ts files fall under file-based metadata conventions, and Next.js automatically turns them into the /sitemap.xml and /robots.txt endpoints. Likewise, app/icon.png and app/opengraph-image.tsx are recognized too.

Does generateMetadata slow down page loads?

Usually not. Its data calls hit the same cache as the page component (request memoization), so the same fetch won't make two network requests. Still, avoid unnecessarily heavy queries and prefer cacheable data sources where possible.

Want to set up your site's Next.js SEO foundation from scratch, or audit your existing metadata? Let's build the App Router, Open Graph and canonical structure around your project — get in touch with me.

Bu kategorideki tüm yazılar →

Devamı için