Sayfa İçi SEO Rehberi: Schema.org ve JSON-LD
Rich snippets için yapısal veri implementasyonu. Article, FAQ, HowTo, Product schema örnekleri ve Next.js ile otomasyon.
Sayfa İçi SEO Rehberi: Schema.org ve JSON-LD
Yapısal veri (structured data), arama motorlarına içeriğinizin ne olduğunu açıkça söylemenin yoludur. Doğru schema markup ile arama sonuçlarında rich snippets kazanabilir, görünürlüğünüzü artırabilirsiniz. Bu yazıda Schema.org standartlarını ve Next.js ile otomatik implementasyonu inceliyoruz.
Bu yazı, 5 parçalık Sayfa İçi SEO Rehberi serisinin dördüncü bölümüdür. Önceki yazılarda Arama Niyeti, Teknik Yapı ve İçerik Mimarisi konularını ele almıştık.
İçindekiler
- Yapısal Veri Nedir?
- JSON-LD, Microdata ve RDFa Karşılaştırma
- Next.js ile Schema Component
- Article ve BlogPosting Schema
- FAQ Schema
- HowTo Schema
- Product ve Review Schema
- Organization ve LocalBusiness
- BreadcrumbList Schema
- Schema Validation ve Test
- Pratik Checklist
- Sık Sorulan Sorular
- Sonuç
Yapısal Veri Nedir?
Yapısal veri, web sayfalarındaki içeriği makinelerin anlayabileceği formatta işaretlemektir. Google, Bing ve diğer arama motorları bu veriyi kullanarak:
- Rich snippets gösterir (yıldızlar, fiyat, FAQ)
- Knowledge Graph'e veri ekler
- Voice search sonuçlarını iyileştirir
- İçeriğinizi daha iyi anlar ve kategorize eder
Rich Snippet Örnekleri
| Schema Tipi | SERP'te Görünüm |
|---|---|
| Article | Yazar, tarih, görsel |
| Product | Fiyat, stok durumu, yıldız |
| FAQ | Açılır soru-cevaplar |
| HowTo | Adım adım rehber |
| Recipe | Kalori, pişirme süresi |
| Event | Tarih, konum, bilet |
JSON-LD, Microdata ve RDFa Karşılaştırma
Üç yapısal veri formatı vardır:
JSON-LD (Önerilen)
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "SEO Rehberi"
}
</script>Avantajları:
- HTML'den bağımsız,
<script>içinde - Bakımı kolay
- Google'ın önerdiği format
- Component bazlı mimariye uygun
Microdata
<article itemscope itemtype="https://schema.org/Article">
<h1 itemprop="headline">SEO Rehberi</h1>
</article>Dezavantajları:
- HTML ile iç içe, dağınık
- Bakımı zor
- Component yapısını bozabilir
RDFa
<article vocab="https://schema.org/" typeof="Article">
<h1 property="headline">SEO Rehberi</h1>
</article>Durumu: Nadir kullanılır, JSON-LD tercih edilir.
Next.js ile Schema Component
Yeniden kullanılabilir bir schema bileşeni oluşturalım:
// components/JsonLd.tsx
interface JsonLdProps {
data: Record<string, unknown>;
}
export function JsonLd({ data }: JsonLdProps) {
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(data)
}}
/>
);
}Schema Generator Library
Daha güvenli tip kontrolü için bir generator:
// lib/schema.ts
const SITE_URL = 'https://beydemir.dev';
interface Author {
name: string;
url?: string;
}
interface ArticleSchemaInput {
title: string;
description: string;
slug: string;
date: string;
author: Author;
image: string;
tags?: string[];
}
export function generateArticleSchema(input: ArticleSchemaInput) {
return {
'@context': 'https://schema.org',
'@type': 'Article',
headline: input.title,
description: input.description,
image: input.image,
datePublished: input.date,
dateModified: input.date,
author: {
'@type': 'Person',
name: input.author.name,
url: input.author.url || SITE_URL,
},
publisher: {
'@type': 'Organization',
name: 'Beydemir.dev',
url: SITE_URL,
logo: {
'@type': 'ImageObject',
url: `${SITE_URL}/logo.png`,
},
},
mainEntityOfPage: {
'@type': 'WebPage',
'@id': `${SITE_URL}/blog/${input.slug}`,
},
keywords: input.tags?.join(', '),
};
}Article ve BlogPosting Schema
Blog yazıları için en temel schema türüdür.
Kullanım
// app/blog/[slug]/page.tsx
import { JsonLd } from '@/components/JsonLd';
import { generateArticleSchema } from '@/lib/schema';
import { getPostBySlug } from '@/lib/mdx';
export default async function BlogPost({ params }: Props) {
const post = await getPostBySlug(params.slug);
const articleSchema = generateArticleSchema({
title: post.title,
description: post.excerpt,
slug: post.slug,
date: post.date,
author: {
name: post.author,
url: 'https://beydemir.dev',
},
image: post.imageUrl,
tags: post.tags,
});
return (
<>
<JsonLd data={articleSchema} />
<article>
<h1>{post.title}</h1>
{/* İçerik */}
</article>
</>
);
}Article vs BlogPosting
- Article: Genel makaleler, haber yazıları
- BlogPosting: Blog yazıları (Article'ın alt türü)
- NewsArticle: Haber siteleri için
Pratikte, Article çoğu durumda yeterlidir.
FAQ Schema
FAQ schema, arama sonuçlarında açılır soru-cevaplar gösterir ve SERP'te daha fazla alan kaplar.
FAQ Schema Generator
// lib/schema.ts
interface FAQItem {
question: string;
answer: string;
}
export function generateFAQSchema(items: FAQItem[]) {
return {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: items.map(item => ({
'@type': 'Question',
name: item.question,
acceptedAnswer: {
'@type': 'Answer',
text: item.answer,
},
})),
};
}MDX'ten Otomatik FAQ Çıkarma
Blog yazılarındaki FAQ bölümünü otomatik parse edin:
// lib/faq-extractor.ts
interface FAQItem {
question: string;
answer: string;
}
export function extractFAQFromContent(content: string): FAQItem[] {
const faqSection = content.match(
/## Sık Sorulan Sorular([\s\S]*?)(?=##|$)/
);
if (!faqSection) return [];
const faqContent = faqSection[1];
const items: FAQItem[] = [];
// ### ile başlayan soruları bul
const questionRegex = /### (.+?\?)\s*\n([\s\S]*?)(?=###|$)/g;
let match;
while ((match = questionRegex.exec(faqContent)) !== null) {
items.push({
question: match[1].trim(),
answer: match[2].trim(),
});
}
return items;
}
// Kullanım
const faqItems = extractFAQFromContent(post.content);
if (faqItems.length > 0) {
const faqSchema = generateFAQSchema(faqItems);
// Schema'yı ekle
}HowTo Schema
Adım adım rehberler için HowTo schema, arama sonuçlarında numbered steps gösterir.
HowTo Schema Generator
// lib/schema.ts
interface HowToStep {
name: string;
text: string;
image?: string;
}
interface HowToInput {
title: string;
description: string;
totalTime?: string; // ISO 8601 format: "PT30M"
steps: HowToStep[];
tools?: string[];
supplies?: string[];
}
export function generateHowToSchema(input: HowToInput) {
return {
'@context': 'https://schema.org',
'@type': 'HowTo',
name: input.title,
description: input.description,
totalTime: input.totalTime,
tool: input.tools?.map(tool => ({
'@type': 'HowToTool',
name: tool,
})),
supply: input.supplies?.map(supply => ({
'@type': 'HowToSupply',
name: supply,
})),
step: input.steps.map((step, index) => ({
'@type': 'HowToStep',
position: index + 1,
name: step.name,
text: step.text,
image: step.image,
})),
};
}Örnek Kullanım
const howToSchema = generateHowToSchema({
title: 'Next.js Projesi Nasıl Kurulur',
description: 'Sıfırdan Next.js projesi oluşturma rehberi',
totalTime: 'PT15M',
tools: ['Node.js', 'npm veya yarn', 'Kod editörü'],
steps: [
{
name: 'Node.js Kurulumu',
text: 'nodejs.org adresinden Node.js indirin ve kurun.',
},
{
name: 'Proje Oluşturma',
text: 'Terminal açın ve npx create-next-app@latest komutunu çalıştırın.',
},
{
name: 'Geliştirme Sunucusu',
text: 'npm run dev komutuyla projeyi başlatın.',
},
],
});Product ve Review Schema
E-ticaret siteleri için Product schema, fiyat ve stok bilgisi gösterir.
Product Schema
// lib/schema.ts
interface ProductInput {
name: string;
description: string;
image: string;
sku: string;
brand: string;
price: number;
currency: string;
inStock: boolean;
rating?: {
average: number;
count: number;
};
}
export function generateProductSchema(input: ProductInput) {
const schema: Record<string, unknown> = {
'@context': 'https://schema.org',
'@type': 'Product',
name: input.name,
description: input.description,
image: input.image,
sku: input.sku,
brand: {
'@type': 'Brand',
name: input.brand,
},
offers: {
'@type': 'Offer',
price: input.price,
priceCurrency: input.currency,
availability: input.inStock
? 'https://schema.org/InStock'
: 'https://schema.org/OutOfStock',
url: typeof window !== 'undefined' ? window.location.href : '',
},
};
// Rating varsa ekle
if (input.rating) {
schema.aggregateRating = {
'@type': 'AggregateRating',
ratingValue: input.rating.average,
reviewCount: input.rating.count,
};
}
return schema;
}Review Schema
interface ReviewInput {
author: string;
rating: number;
reviewBody: string;
datePublished: string;
productName: string;
}
export function generateReviewSchema(input: ReviewInput) {
return {
'@context': 'https://schema.org',
'@type': 'Review',
author: {
'@type': 'Person',
name: input.author,
},
reviewRating: {
'@type': 'Rating',
ratingValue: input.rating,
bestRating: 5,
},
reviewBody: input.reviewBody,
datePublished: input.datePublished,
itemReviewed: {
'@type': 'Product',
name: input.productName,
},
};
}Organization ve LocalBusiness
Kurumsal siteler ve yerel işletmeler için temel schema türleri.
Organization Schema
export function generateOrganizationSchema() {
return {
'@context': 'https://schema.org',
'@type': 'Organization',
name: 'Beydemir.dev',
url: 'https://beydemir.dev',
logo: 'https://beydemir.dev/logo.png',
sameAs: [
'https://twitter.com/furkanbeydemir',
'https://github.com/furkanbeydemir',
'https://linkedin.com/in/furkanbeydemir',
],
contactPoint: {
'@type': 'ContactPoint',
email: 'hello@beydemir.dev',
contactType: 'customer service',
},
};
}LocalBusiness Schema
interface LocalBusinessInput {
name: string;
address: {
street: string;
city: string;
region: string;
postalCode: string;
country: string;
};
phone: string;
openingHours: string[];
geo: {
latitude: number;
longitude: number;
};
}
export function generateLocalBusinessSchema(input: LocalBusinessInput) {
return {
'@context': 'https://schema.org',
'@type': 'LocalBusiness',
name: input.name,
address: {
'@type': 'PostalAddress',
streetAddress: input.address.street,
addressLocality: input.address.city,
addressRegion: input.address.region,
postalCode: input.address.postalCode,
addressCountry: input.address.country,
},
telephone: input.phone,
openingHoursSpecification: input.openingHours.map(hours => ({
'@type': 'OpeningHoursSpecification',
dayOfWeek: hours.split(' ')[0],
opens: hours.split(' ')[1]?.split('-')[0],
closes: hours.split(' ')[1]?.split('-')[1],
})),
geo: {
'@type': 'GeoCoordinates',
latitude: input.geo.latitude,
longitude: input.geo.longitude,
},
};
}BreadcrumbList Schema
Breadcrumb schema, arama sonuçlarında sayfa yolunu gösterir.
interface BreadcrumbItem {
name: string;
url: string;
}
export function generateBreadcrumbSchema(items: BreadcrumbItem[]) {
return {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: items.map((item, index) => ({
'@type': 'ListItem',
position: index + 1,
name: item.name,
item: item.url,
})),
};
}
// Kullanım
const breadcrumbSchema = generateBreadcrumbSchema([
{ name: 'Ana Sayfa', url: 'https://beydemir.dev' },
{ name: 'Blog', url: 'https://beydemir.dev/blog' },
{ name: 'SEO Rehberi', url: 'https://beydemir.dev/blog/seo-rehberi' },
]);Schema Validation ve Test
Google Rich Results Test
Google'ın resmi test aracı: Rich Results Test
Schema Markup Validator
Schema.org'un validator'ı: Schema Validator
CI/CD Schema Validation
Build sürecinde schema kontrolü:
// scripts/validate-schemas.ts
import { getAllPosts } from '../lib/mdx';
import { generateArticleSchema } from '../lib/schema';
interface ValidationError {
slug: string;
field: string;
message: string;
}
async function validateAllSchemas() {
const posts = await getAllPosts();
const errors: ValidationError[] = [];
for (const post of posts) {
const schema = generateArticleSchema({
title: post.title,
description: post.excerpt,
slug: post.slug,
date: post.date,
author: { name: post.author },
image: post.imageUrl,
});
// Zorunlu alanları kontrol et
if (!schema.headline) {
errors.push({
slug: post.slug,
field: 'headline',
message: 'Başlık eksik',
});
}
if (schema.headline && schema.headline.length > 110) {
errors.push({
slug: post.slug,
field: 'headline',
message: 'Başlık 110 karakteri aşıyor',
});
}
if (!schema.image) {
errors.push({
slug: post.slug,
field: 'image',
message: 'Görsel URL eksik',
});
}
if (!schema.datePublished) {
errors.push({
slug: post.slug,
field: 'datePublished',
message: 'Tarih eksik',
});
}
}
if (errors.length > 0) {
console.error('Schema hataları bulundu:');
errors.forEach(e => {
console.error(` - ${e.slug}: ${e.field} - ${e.message}`);
});
process.exit(1);
}
console.log(`✓ ${posts.length} yazı için schema doğrulandı`);
}
validateAllSchemas();Package.json Script
{
"scripts": {
"schema:validate": "tsx scripts/validate-schemas.ts"
}
}Pratik Checklist
- Her sayfa için uygun schema türü belirlendi
- Article/BlogPosting schema tüm blog yazılarında var
- FAQ bölümü olan yazılarda FAQ schema eklendi
- Adım adım rehberlerde HowTo schema kullanıldı
- Ürün sayfalarında Product schema mevcut
- Ana sayfada Organization schema var
- Breadcrumb schema tüm sayfalarda var
- Schema'lar Google Rich Results Test ile doğrulandı
- CI/CD'de schema validation eklendi
- Zorunlu alanlar (headline, image, date) mevcut
Sık Sorulan Sorular
Schema eklediğimde rich snippet garantili mi?
Hayır. Schema, Google'a yapılandırılmış veri sağlar ama rich snippet gösterme kararı Google'a aittir. Doğru schema, şansı artırır ama garanti etmez.
Bir sayfada birden fazla schema olabilir mi?
Evet. Article + FAQ + BreadcrumbList aynı sayfada olabilir. Her biri ayrı <script type="application/ld+json"> içinde veya bir array olarak:
[
{ "@type": "Article", ... },
{ "@type": "FAQPage", ... },
{ "@type": "BreadcrumbList", ... }
]Schema hataları sıralamayı etkiler mi?
Hatalı schema doğrudan sıralamayı düşürmez ama rich snippet gösterilmez. Ayrıca Google Search Console'da uyarılar görürsünüz.
En önemli schema türleri hangileri?
Kullanım sıklığına göre: Article, BreadcrumbList, FAQ, Organization, Product. Önce bunları implement edin.
Schema'yı manuel mi yoksa otomatik mi oluşturmalı?
Otomatik. Bu yazıdaki gibi generator fonksiyonları kullanın. Manuel yazım hataya açıktır ve bakımı zordur.
Sonuç
Schema.org ve JSON-LD, modern SEO'nun olmazsa olmazıdır. Bu yazıda öğrendikleriniz:
- JSON-LD Google'ın önerdiği format
- Article schema blog yazıları için temel
- FAQ ve HowTo SERP'te ekstra alan kazandırır
- Product schema e-ticaret için kritik
- Validation CI/CD sürecine dahil edilmeli
Bir sonraki ve son yazıda, JavaScript SEO ve Performans konularını inceleyeceğiz: SSR vs CSR, Core Web Vitals, crawl budget ve edge SEO.
Seri Navigasyonu:
- Arama Niyeti ve Semantik SEO
- Teknik Sayfa Yapısı ve Metadata
- İçerik Mimarisi ve Internal Linking
- Schema.org ve JSON-LD (Bu yazı)
- JavaScript SEO ve Performans
Projenizi Hayata Geçirelim
Web sitesi, mobil uygulama veya yapay zeka çözümü mü arıyorsunuz? Fikirlerinizi birlikte değerlendirelim.
Ücretsiz Danışmanlık Alın