Next.js 13+ ile gelen React Server Components, React uygulamalarında component'ların nerede çalıştığı konusundaki temel varsayımı değiştirdi. Artık her bileşen ya sunucuda ya da tarayıcıda çalışır; ikisi arasındaki sınır doğru çizildiğinde hem performans hem de geliştirici deneyimi kazanır.
Temel Fark
// PLAINTEXT //
Server Component Client Component──────────────────────────────────────────────────❌ useState, useEffect ✅ useState, useEffect❌ Browser API'leri (window, DOM) ✅ Browser API'leri❌ Event handler'lar ✅ Event handler'lar✅ async/await doğrudan ❌ useEffect içinde fetch✅ Veritabanı erişimi ❌ API üzerinden erişmeli✅ .env secret'larına erişim ❌ Yalnızca NEXT_PUBLIC_ değişkenler✅ Bundle'a katkısı yok ❌ Bundle size artırır
Ne Zaman Server Component?
// TSX //
// ✅ Server Component — veri getirme, statik renderasync function BlogListesi() { // Doğrudan DB — API round-trip yok, SECRET erişilebilir const makaleler = await db.makale.findMany({ where: { yayinlandi: true } }); return ( <ul> {makaleler.map(m => ( // MakaleKart client component olabilir — interactivity için <MakaleKart key={m.id} makale={m} /> ))} </ul> );}// ✅ Server Component — layout, navigasyon, statik UIasync function Layout({ children }: { children: React.ReactNode }) { const kullanici = await mevcutKullanici(); // Çerez okuma return ( <div> <Header kullanici={kullanici} /> <main>{children}</main> <Footer /> </div> );}
// ❌ Yanlış — tüm sayfa client component oldu'use client';async function BlogSayfasi() { // async client component = hata! const [filtre, setFiltre] = useState(''); const makaleler = await db.makale.findMany(); // ❌ Client'ta DB erişimi yok // ...}// ✅ Doğru — sınırı mümkün olduğunca aşağı it// page.tsx — Server Component (varsayılan)async function BlogSayfasi() { const makaleler = await db.makale.findMany({ where: { yayinlandi: true } }); return ( <div> <AramaFiltresi /> {/* Client — interactivity */} <MakaleListesi makaleler={makaleler} /> {/* Server — statik */} </div> );}// components/AramaFiltresi.tsx'use client';function AramaFiltresi() { const [filtre, setFiltre] = useState(''); const router = useRouter(); function ara(e: React.FormEvent) { e.preventDefault(); router.push(`/blog?q=${filtre}`); } return ( <form onSubmit={ara}> <input value={filtre} onChange={e => setFiltre(e.target.value)} /> </form> );}
Server Component'tan Client Component'a Veri Aktarma
// TSX //
// Server → Client: sadece serileştirilebilir prop'larasync function MakaleSayfasi({ params }: { params: { slug: string } }) { const makale = await db.makale.findUnique({ where: { slug: params.slug } }); return ( <article> <h1>{makale.baslik}</h1> {/* ✅ Düz veri aktar */} <BegeniButonu makaleSlugi={makale.slug} baslangiçSayisi={makale.begeniSayisi} /> {/* ❌ Class instance, function, Promise geçiremezsiniz */} </article> );}
Bundle Size Farkı
Bir bileşeni Server Component yaparak:
▸Bileşenin kodu tarayıcıya gönderilmez
▸moment, lodash, büyük kütüphaneler bundle'a girmez
▸İlk yükleme daha hızlı; Time to Interactive düşer
// PLAINTEXT //
// react-pdf gibi ağır kütüphane — Server Component olarak kullanınasync function PDFOnizleme({ url }: { url: string }) { const pdf = await parsePDF(url); // ağır kütüphane, bundle'a girmez return <div>{pdf.metin.substring(0, 200)}...</div>;}
Karar Ağacı
// PLAINTEXT //
Bileşen useState/useEffect/event kullanıyor mu?├── Evet → 'use client' ekle → Client Component└── Hayır → Server Component (varsayılan) ├── DB/secret erişiyor mu? → Server Component ✅ ├── async veri çekiyor mu? → Server Component ✅ └── Statik HTML mi? → Server Component ✅
Özet
Server Components: veri çekme, statik render, ağır kütüphane kullanımı. Client Components: interactivity, browser API, gerçek zamanlı durum. Sınırı mümkün olduğunca aşağıya, yaprak bileşenlere itin. Doğru sınır = küçük bundle + hızlı ilk yükleme + sıfır gereksiz client JavaScript.