Next.js Dinamik Routing: SEO Dostu URL Yapısı Oluşturma
Next.js App Router ile dinamik routing kullanarak SEO dostu URL yapısı oluşturmayı öğrenin. Slug parametreleri, catch-all routes ve metadata optimizasyonu.
Next.js, modern web uygulamaları için en popüler React framework'lerinden biridir. App Router ile gelen dinamik routing özellikleri, SEO dostu URL yapıları oluşturmak için güçlü araçlar sunar. Bu rehberde, Next.js ile slug tabanlı URL'ler oluşturmayı ve SEO optimizasyonunu detaylı olarak inceleyeceğiz.
Next.js App Router Temelleri
Next.js 13+ ile gelen App Router, dosya tabanlı routing sistemi kullanır:
app/
├── page.tsx # / (Ana sayfa)
├── blog/
│ ├── page.tsx # /blog
│ └── [slug]/
│ └── page.tsx # /blog/makale-basligi
├── products/
│ └── [category]/
│ └── [id]/
│ └── page.tsx # /products/electronics/123Dinamik Route Parametreleri
Tek Parametre [slug]
Blog yazıları gibi tekil içerikler için:
// app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation';
interface PageProps {
params: { slug: string };
}
export default async function BlogPost({ params }: PageProps) {
const post = await getPost(params.slug);
if (!post) {
notFound();
}
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
);
}Catch-All Routes [...slug]
Çoklu segment URL'ler için:
// app/docs/[...slug]/page.tsx
// Eşleşir: /docs/getting-started
// /docs/api/reference
// /docs/guides/advanced/routing
interface PageProps {
params: { slug: string[] };
}
export default function DocsPage({ params }: PageProps) {
// params.slug = ['guides', 'advanced', 'routing']
const path = params.slug.join('/');
return <div>Docs: {path}</div>;
}Optional Catch-All [[...slug]]
Root path dahil yakalama:
// app/shop/[[...slug]]/page.tsx
// Eşleşir: /shop
// /shop/category
// /shop/category/productSEO Metadata Optimizasyonu
generateMetadata Fonksiyonu
Dinamik sayfalar için SEO metadata oluşturma:
// app/blog/[slug]/page.tsx
import { Metadata } from 'next';
export async function generateMetadata(
{ params }: PageProps
): Promise<Metadata> {
const post = await getPost(params.slug);
if (!post) {
return { title: 'Not Found' };
}
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
type: 'article',
publishedTime: post.publishedAt,
url: `https://example.com/blog/${params.slug}`,
images: [{
url: post.featuredImage,
width: 1200,
height: 630,
alt: post.title,
}],
},
twitter: {
card: 'summary_large_image',
title: post.title,
description: post.excerpt,
images: [post.featuredImage],
},
};
}Static Generation ile SEO
generateStaticParams
Build zamanında statik sayfalar oluşturma:
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await getAllPosts();
return posts.map((post) => ({
slug: post.slug,
}));
}
// Bu fonksiyon build zamanında çalışır ve
// tüm blog yazıları için statik HTML oluştururÇoklu Dil Desteği
// app/[locale]/blog/[slug]/page.tsx
export async function generateStaticParams() {
const locales = ['tr', 'en', 'de'];
const posts = await getAllPosts();
return locales.flatMap((locale) =>
posts.map((post) => ({
locale,
slug: post.slugs[locale],
}))
);
}Canonical URL Yönetimi
// app/blog/[slug]/page.tsx
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const baseUrl = 'https://example.com';
return {
alternates: {
canonical: `${baseUrl}/blog/${params.slug}`,
languages: {
'en': `${baseUrl}/en/blog/${params.slug}`,
'tr': `${baseUrl}/tr/blog/${params.slug}`,
'de': `${baseUrl}/de/blog/${params.slug}`,
},
},
};
}Slug Oluşturma Fonksiyonu
// lib/utils/slug.ts
export function generateSlug(text: string): string {
// Türkçe karakter dönüşümü
const charMap: Record<string, string> = {
'ş': 's', 'ğ': 'g', 'ü': 'u', 'ö': 'o', 'ç': 'c', 'ı': 'i',
'Ş': 's', 'Ğ': 'g', 'Ü': 'u', 'Ö': 'o', 'Ç': 'c', 'İ': 'i',
};
return text
.toLowerCase()
.replace(/[şğüöçıŞĞÜÖÇİ]/g, (char) => charMap[char] || char)
.replace(/[^a-z0-9\s-]/g, '')
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.replace(/^-|-$/g, '');
}
// Kullanım
const slug = generateSlug('Türkçe Başlık Örneği');
// Çıktı: 'turkce-baslik-ornegi'Sitemap Oluşturma
// app/sitemap.ts
import { MetadataRoute } from 'next';
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const baseUrl = 'https://example.com';
const posts = await getAllPosts();
const blogUrls = posts.map((post) => ({
url: `${baseUrl}/blog/${post.slug}`,
lastModified: new Date(post.updatedAt),
changeFrequency: 'weekly' as const,
priority: 0.8,
}));
return [
{
url: baseUrl,
lastModified: new Date(),
changeFrequency: 'daily',
priority: 1,
},
{
url: `${baseUrl}/blog`,
lastModified: new Date(),
changeFrequency: 'daily',
priority: 0.9,
},
...blogUrls,
];
}Robots.txt Oluşturma
// app/robots.ts
import { MetadataRoute } from 'next';
export default function robots(): MetadataRoute.Robots {
return {
rules: {
userAgent: '*',
allow: '/',
disallow: ['/api/', '/admin/', '/private/'],
},
sitemap: 'https://example.com/sitemap.xml',
};
}Loading ve Error States
Loading UI
// app/blog/[slug]/loading.tsx
export default function Loading() {
return (
<div className="animate-pulse">
<div className="h-8 bg-gray-200 rounded w-3/4 mb-4"></div>
<div className="h-4 bg-gray-200 rounded w-full mb-2"></div>
<div className="h-4 bg-gray-200 rounded w-5/6"></div>
</div>
);
}Not Found Sayfası
// app/blog/[slug]/not-found.tsx
export default function NotFound() {
return (
<div className="text-center py-20">
<h1 className="text-4xl font-bold">404</h1>
<p>İçerik bulunamadı</p>
</div>
);
}Best Practices
- Lowercase URL'ler: Tüm slug'ları küçük harfe dönüştürün
- Hyphens kullanın: Kelime ayırıcı olarak tire (-) tercih edin
- Kısa tutun: URL'leri 75 karakterin altında tutun
- Anahtar kelime ekleyin: Slug'a ana anahtar kelimeyi dahil edin
- Stopword'leri kaldırın: ve, ile, için gibi kelimeleri çıkarın
Sonuç
Next.js App Router ile dinamik routing, SEO dostu URL yapıları oluşturmak için güçlü ve esnek bir sistem sunar. generateMetadata ve generateStaticParams fonksiyonlarını kullanarak, hem arama motoru optimizasyonu hem de kullanıcı deneyimi açısından mükemmel sonuçlar elde edebilirsiniz. Slug oluşturma fonksiyonlarınızı doğru yapılandırarak ve canonical URL'leri düzgün yöneterek, Next.js projenizin SEO performansını maksimize edebilirsiniz.