Next.js Projelerimde Blog İçin Neden Notion Tercih Ediyorum
Strapi, Contentful gibi headless CMS'ler varken neden Notion'ı tercih ediyorum? Pratik deneyimlerim ve karşılaştırmam.

Problem
Çoğu Next.js blog'u MDX veya Markdown dosyalarıyla çalışıyor. Basit projeler için iyi, ama birden fazla projeye blog yazıyorsan veya teknik olmayan birinin de içerik eklemesini istiyorsan sorun başlıyor:
- Her yazı için git commit + deploy gerekiyor
- Görsel eklemek zahmetli
- Mobil'den yazı yazmak neredeyse imkansız
- Draft/publish akışı yok
Peki Neden Strapi veya Contentful Değil?
Bu soruyu kendime çok sordum. İkisini de denedim, ikisinin de güçlü yanları var. Ama benim kullanım senaryomda Notion kazandı.
Strapi
Strapi açık kaynak, self-hosted bir headless CMS. İlk bakışta harika görünüyor:
- Kendi sunucunda çalışıyor, veri sende kalıyor
- Custom content type'lar oluşturabiliyorsun
- REST ve GraphQL API'leri built-in
Ama pratikte şu sorunlarla karşılaştım:
- Hosting maliyeti. Strapi bir Node.js uygulaması, sunucu gerekiyor. Küçük bir blog için aylık $5-15 sunucu maliyeti çıkıyor. Notion'da bu sıfır.
- Bakım yükü. Strapi'yi güncel tutmak gerekiyor. Major version upgrade'leri (v4 → v5 gibi) breaking change'lerle dolu ve migrasyon süreci zahmetli.
- Editör deneyimi. Strapi'nin admin paneli fonksiyonel ama Notion'ın editörüyle kıyaslanamaz. Notion'da yazmak gerçekten keyifli, Strapi'de form doldurur gibi hissediyorsun.
- Setup süresi. Strapi'yi kurmak, content type tanımlamak, API permission'ları ayarlamak, deploy etmek... Minimum 2-3 saat. Notion'da database oluşturup API key almak 5 dakika.
Contentful
Contentful managed bir SaaS. Hosting derdin yok, ama başka dertlerin var:
- Ücretsiz plan sınırlı. 25K API call/ay ve 5 kullanıcı. Trafik arttığında hemen ücretli plana geçmen gerekiyor.
- Pricing agresif. Team planı $300/ay'dan başlıyor. Bir blog için bu çok fazla.
- Content modeling karmaşık. Basit bir blog yazısı için bile content type, field tanımlamaları, validation rule'ları ayarlamak gerekiyor.
- Vendor lock-in. İçeriklerini çıkarmak istediğinde export formatları sınırlı.
Sanity, Ghost, diğerleri?
- Sanity güzel ama kendi GROQ query dilini öğrenmek gerekiyor. Küçük projeler için overkill.
- Ghost blog-spesifik ve güzel, ama self-hosted versiyonu yine sunucu gerektiriyor. Pro planı $9/ay'dan başlıyor.
Notion Neden Kazanıyor?
Notion'ı zaten günlük not alma, proje yönetimi ve dokümantasyon için kullanıyordum. Blog yazılarını da aynı yerden yönetebilmek büyük kolaylık. Ama asıl sebepler:
- Sıfır maliyet. Notion'ın ücretsiz planı kişisel kullanım için yeterli. API erişimi de ücretsiz.
- Sıfır bakım. Hosting, güncelleme, veritabanı yönetimi yok. Notion bunu hallediyor.
- Harika editör. Kod blokları, tablo, toggle, callout, embed gibi bloklar native. Markdown'dan çok daha esnek ve Strapi'nin form-based editöründen çok daha keyifli.
- Mobil erişim. Telefon'dan yazı yazıp yayınlayabiliyorum. Git'e dokunmam gerekmiyor.
- 5 dakikada kurulum. Database oluştur, API key al, Next.js'e bağla. Bu kadar.
- Zaten kullanıyorum. Ekstra bir tool öğrenmeme gerek yok. Notion zaten günlük iş akışımın parçası.
Nasıl Çalışıyor?
Temel akış:
- Notion Database oluşturuyorum (Title, Slug, Date, Tags, Status, Cover property'leri ile)
- Notion API ile Next.js'den bu veritabanını çekiyorum
- Block render ile Notion block'larını React component'lerine dönüştürüyorum
- ISR ile sayfa her 60 saniyede otomatik güncelleniyor
lib/notion.ts
import { Client } from '@notionhq/client'
const notion = new Client({ auth: process.env.NOTION_TOKEN })
export async function getPosts() {
const response = await notion.databases.query({
database_id: process.env.NOTION_DATABASE_ID,
filter: {
property: 'Status',
select: { equals: 'Published' }
},
sorts: [{ property: 'Date', direction: 'descending' }]
})
return response.results
}
Notion Block'larını Render Etmek
Notion API her paragrafı, başlığı, kodu ayrı bir "block" olarak döndürüyor. Bunları React component'lerine map'liyorum:
components/NotionBlock.tsx
function renderBlock(block) {
switch (block.type) {
case 'paragraph':
return <p>{renderRichText(block.paragraph.rich_text)}</p>
case 'heading_2':
return <h2>{renderRichText(block.heading_2.rich_text)}</h2>
case 'code':
return <CodeBlock language={block.code.language}>
{block.code.rich_text[0].plain_text}
</CodeBlock>
case 'image':
return <img src={block.image.file?.url || block.image.external?.url} />
case 'callout':
return <Callout emoji={block.callout.icon?.emoji}>
{renderRichText(block.callout.rich_text)}
</Callout>
default:
return null
}
}
Karşılaştırma Tablosu
| Notion | Strapi | Contentful | |
|---|---|---|---|
| Maliyet | Ücretsiz | $5-15/ay sunucu | $0-300/ay |
| Setup süresi | 5 dk | 2-3 saat | 30 dk |
| Hosting | Yok (SaaS) | Self-hosted | Yok (SaaS) |
| Editör kalitesi | Mükemmel | Orta | İyi |
| Mobil yazım | Var | Sınırlı | Var |
| API | REST | REST + GraphQL | REST + GraphQL |
| Bakım | Sıfır | Yüksek | Düşük |
| Image CDN | Dahili (expire'lı) | Kendi sunucun | Dahili |
Dikkat Edilecekler
- Notion image URL'leri expire oluyor.
filetipindeki görsellerin URL'si 1 saat geçerli. Build sırasında bunları kendi storage'ına kaydetmen lazım.externaltiplerde bu sorun yok. - ISR + On-Demand Revalidation kullan. Notion'da yazıyı güncellediğinde webhook ile Next.js'e haber verip o sayfayı yeniden build ettirebilirsin.
- Rate limit. Notion API saniyede 3 request ile sınırlı. Sayfa sayısı artarsa caching stratejin olmalı.
- Notion özelleştirme sınırlı. Custom field type'lar oluşturamazsın. Strapi bu konuda çok daha esnek. Ama bir blog için Notion'ın sunduğu property'ler fazlasıyla yeterli.
Sonuç
Eğer basit-orta karmaşıklıkta bir blog için CMS arıyorsan ve zaten Notion kullanıyorsan, başka bir şeye gerek yok. Strapi veya Contentful'ü kurup bakımını yapmak yerine o zamanı içerik yazmaya harca.
Eğer custom content type'lar, karmaşık ilişkiler ve multi-tenant yapılar gerekiyorsa Strapi mantıklı. Ama sadece blog + birkaç sayfa için Notion fazlasıyla yeterli.
Sonraki yazılarda adım adım Notion API entegrasyonu tutorial'ı ve webhook ile on-demand revalidation kurulumunu paylaşacağım.