İçeriğe geç
nextjs

Next.js Metadata API ile SEO Optimizasyonu

Tarık Tunç
Next.js Metadata API ile SEO Optimizasyonu

Next.js Metadata API ile SEO Optimizasyonu

Arama motoru optimizasyonu (SEO), web uygulamalarının keşfedilebilirliği için kritik öneme sahiptir. Next.js, Metadata API ile title, description, Open Graph, Twitter Card ve daha birçok meta etiketi kolayca yönetmenizi sağlar. Bu rehberde Next.js'in Metadata API'sini kullanarak SEO'yu nasıl optimize edeceğinizi adım adım göstereceğiz.

Statik Metadata

En basit metadata tanımı, bir layout veya page dosyasından metadata objesi export etmektir:

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

export const metadata: Metadata = {
  title: {
    default: 'Tarik Tunç Blog',
    template: '%s | Tarik Tunç Blog', // Alt sayfalarda kullanılır
  },
  description: 'Web geliştirme, React ve Next.js hakkında Türkçe blog yazıları',
  keywords: ['Next.js', 'React', 'Web Geliştirme', 'TypeScript'],
  authors: [{ name: 'Tarik Tunç', url: 'https://tariktunc.com' }],
  creator: 'Tarik Tunç',
  metadataBase: new URL('https://tariktunc.com'),
  openGraph: {
    type: 'website',
    locale: 'tr_TR',
    url: 'https://tariktunc.com',
    siteName: 'Tarik Tunç Blog',
    title: 'Tarik Tunç Blog',
    description: 'Web geliştirme hakkında Türkçe içerikler',
    images: [
      {
        url: '/og-image.png',
        width: 1200,
        height: 630,
        alt: 'Tarik Tunç Blog',
      },
    ],
  },
  twitter: {
    card: 'summary_large_image',
    title: 'Tarik Tunç Blog',
    description: 'Web geliştirme hakkında Türkçe içerikler',
    creator: '@tariktunc',
  },
  robots: {
    index: true,
    follow: true,
    googleBot: {
      index: true,
      follow: true,
      'max-video-preview': -1,
      'max-image-preview': 'large',
      'max-snippet': -1,
    },
  },
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="tr">
      <body>{children}</body>
    </html>
  );
}

Dinamik Metadata

Dinamik sayfalarda generateMetadata fonksiyonu ile parametrelere göre metadata oluşturabilirsiniz:

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

async function getPost(slug: string) {
  const res = await fetch(`https://api.example.com/posts/${slug}`);
  return res.json();
}

export async function generateMetadata({
  params,
}: {
  params: { slug: string };
}): Promise<Metadata> {
  const post = await getPost(params.slug);

  return {
    title: post.title, // template ile: "Yazı Başlığı | Tarik Tunç Blog"
    description: post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      type: 'article',
      publishedTime: post.publishedAt,
      modifiedTime: post.updatedAt,
      authors: [post.author.name],
      tags: post.tags,
      images: [
        {
          url: post.coverImage,
          width: 1200,
          height: 630,
          alt: post.title,
        },
      ],
    },
    twitter: {
      card: 'summary_large_image',
      title: post.title,
      description: post.excerpt,
      images: [post.coverImage],
    },
    alternates: {
      canonical: `/blog/${params.slug}`,
    },
  };
}
İpucu: generateMetadata içindeki fetch istekleri, aynı page component'teki fetch istekleri ile otomatik olarak deduplicate edilir. Yani aynı URL'ye iki kez istek gönderilmez.

Title Template

Layout'ta tanımlanan title template, alt sayfalarda otomatik uygulanır:

// app/layout.tsx
export const metadata: Metadata = {
  title: {
    default: 'Tarik Tunç Blog',        // Fallback
    template: '%s | Tarik Tunç Blog',   // Alt sayfalar için
    absolute: 'Özel Başlık',            // Template'i yoksayar
  },
};

// app/about/page.tsx
export const metadata: Metadata = {
  title: 'Hakkımda',
  // Sonuç: "Hakkımda | Tarik Tunç Blog"
};

// app/contact/page.tsx
export const metadata: Metadata = {
  title: {
    absolute: 'İletişim Sayfası', // Template uygulanmaz
  },
  // Sonuç: "İletişim Sayfası"
};

JSON-LD Structured Data

Arama motorlarının içeriğinizi daha iyi anlaması için JSON-LD yapılandırılmış veri ekleyin:

// app/blog/[slug]/page.tsx
export default async function BlogPost({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug);

  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'BlogPosting',
    headline: post.title,
    description: post.excerpt,
    image: post.coverImage,
    datePublished: post.publishedAt,
    dateModified: post.updatedAt,
    author: {
      '@type': 'Person',
      name: post.author.name,
      url: post.author.url,
    },
    publisher: {
      '@type': 'Organization',
      name: 'Tarik Tunç Blog',
      logo: {
        '@type': 'ImageObject',
        url: 'https://tariktunc.com/logo.png',
      },
    },
  };

  return (
    <>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      <article>
        <h1>{post.title}</h1>
        <div dangerouslySetInnerHTML={{ __html: post.content }} />
      </article>
    </>
  );
}

Canonical URL ve Alternates

// app/blog/[slug]/page.tsx
export async function generateMetadata({ params }): Promise<Metadata> {
  return {
    alternates: {
      canonical: `/blog/${params.slug}`,
      languages: {
        'tr-TR': `/tr/blog/${params.slug}`,
        'en-US': `/en/blog/${params.slug}`,
      },
      types: {
        'application/rss+xml': '/feed.xml',
      },
    },
  };
}
Uyarı: Canonical URL'ler, duplicate content sorunlarını önlemek için kritiktir. Her sayfanın benzersiz bir canonical URL'i olduğundan emin olun.

Favicon ve Icons

// app/layout.tsx
export const metadata: Metadata = {
  icons: {
    icon: [
      { url: '/favicon.ico', sizes: 'any' },
      { url: '/icon.svg', type: 'image/svg+xml' },
    ],
    apple: [
      { url: '/apple-icon.png', sizes: '180x180' },
    ],
    shortcut: '/shortcut-icon.png',
  },
  manifest: '/manifest.json',
};

Verification ve Diğer Meta Etiketler

export const metadata: Metadata = {
  verification: {
    google: 'google-site-verification-code',
    yandex: 'yandex-verification-code',
    other: {
      'msvalidate.01': 'bing-verification-code',
    },
  },
  category: 'technology',
  other: {
    'theme-color': '#000000',
  },
};
Bilgi: Next.js Metadata API, head etiketlerini otomatik olarak yönetir. Aynı metadata farklı seviyelerde tanımlanırsa, en yakın (en spesifik) tanım geçerli olur.

SEO Kontrol Listesi

  • Her sayfada benzersiz title ve description olmalı
  • Open Graph ve Twitter Card meta etiketleri eklenmeli
  • Canonical URL tanımlanmalı
  • JSON-LD structured data eklenmeli
  • Sitemap.xml ve robots.txt oluşturulmalı
  • Resimler için alt text kullanılmalı
  • Semantic HTML etiketleri tercih edilmeli
  • Core Web Vitals metrikleri optimize edilmeli

Sonuç

Next.js Metadata API, SEO optimizasyonunu framework seviyesinde ele alarak geliştiricilerin işini büyük ölçüde kolaylaştırır. Statik ve dinamik metadata desteği, title template, JSON-LD entegrasyonu ve otomatik deduplication gibi özelliklerle arama motoru görünürlüğünüzü maksimize edebilirsiniz. Her sayfanız için doğru metadata tanımlamayı alışkanlık haline getirin.

Kaynaklar

İlgili Yazılar