React Server Components (RSC), React'ın mimarisini temelden değiştiren bir paradigma. İlk duyurulduğunda tartışmalara yol açtı, ama 2026'da artık production'da kendini kanıtladı. Bu yazıda RSC'yi teoriden pratiğe, tüm detaylarıyla ele alıyoruz.
React'ın Evrimi: CSR'den RSC'ye
React'ın tarihine bakarsak net bir evrim görüyoruz:
- 2013-2016 — Client-Side Rendering (CSR): Tüm React kodu tarayıcıda çalışıyor. Büyük JavaScript bundle'ları, yavaş ilk yükleme.
- 2016-2020 — Server-Side Rendering (SSR): Next.js ile sunucuda HTML oluşturulup istemciye gönderiliyor. Hydration süreci hâlâ pahalı.
- 2020-2023 — Streaming SSR: React 18 ile HTML parça parça gönderiliyor. Ama hâlâ tüm component'ler istemcide hydrate ediliyor.
- 2023-2026 — React Server Components: Component'ler sunucuda çalışıyor ve istemciye sıfır JavaScript gönderiyor. Sadece interaktif component'ler hydrate ediliyor.
RSC bu evrimde mantıksal bir sonraki adım. Artık her component'in istemcide çalışması gerekmiyor; sunucuda render edilip HTML olarak gönderilebiliyor.
RSC Nasıl Çalışır?
Temel Kavram
RSC'de component'ler ikiye ayrılır:
Server Components (varsayılan):
- Sunucuda çalışır
- İstemciye JavaScript göndermez
- Doğrudan veritabanına, dosya sistemine, API'lere erişebilir
useState,useEffectgibi hook'ları kullanamaz- Event handler'ları kullanamaz
Client Components ("use client" direktifi):
- İstemcide çalışır (hydration ile)
- Interaktivite sağlar
- State, effect ve event handler kullanabilir
- Tarayıcı API'lerine erişebilir
// Server Component — varsayılan, "use client" yok
// Bu component sunucuda çalışır, istemciye JS göndermez
async function ProductPage({ params }: { params: { id: string } }) {
// Doğrudan veritabanı erişimi — API route'a gerek yok
const product = await db.product.findUnique({
where: { id: params.id },
include: { reviews: true, category: true }
});
if (!product) return notFound();
return (
<main>
<h1>{product.name}</h1>
<p>{product.description}</p>
<span>{product.price} TL</span>
{/* Client Component — interaktivite gerekli */}
<AddToCartButton productId={product.id} />
{/* Server Component — sadece veri gösterimi */}
<ReviewList reviews={product.reviews} />
</main>
);
}
// Client Component — "use client" direktifi zorunlu
"use client";
import { useState } from "react";
export function AddToCartButton({ productId }: { productId: string }) {
const [loading, setLoading] = useState(false);
const [added, setAdded] = useState(false);
async function handleClick() {
setLoading(true);
await fetch("/api/cart", {
method: "POST",
body: JSON.stringify({ productId }),
});
setAdded(true);
setLoading(false);
}
return (
<button onClick={handleClick} disabled={loading}>
{loading ? "Ekleniyor..." : added ? "Eklendi!" : "Sepete Ekle"}
</button>
);
}
RSC Wire Format
RSC'nin perde arkasında özel bir wire format var. Sunucu, component ağacını JSON benzeri bir akış formatında serialize eder. Bu format:
- Server Component'lerin render edilmiş HTML'ini içerir
- Client Component'lerin referanslarını (bundle path) içerir
- Props verilerini içerir
- Streaming destekler — Suspense boundary'lerinde chunk'lar halinde gönderilir
Bu sayede sayfa yüklenirken:
- HTML anında görünür (Server Component'ler)
- Client Component'lerin JavaScript'i paralel yüklenir
- Hydration sadece Client Component'lerde gerçekleşir
- Sayfa aşamalı olarak interaktif hale gelir
Performans Etkisi: Gerçek Veriler
RSC'nin performans etkisini somut verilerle gösterelim. Maviona olarak müşteri projelerinde yaptığımız ölçümler:
Senaryo: E-Ticaret Ürün Listesi Sayfası (50 ürün)
| Metrik | Geleneksel CSR | SSR + Hydration | RSC |
|---|---|---|---|
| JavaScript boyutu (gzip) | 180 KB | 180 KB | 42 KB |
| First Contentful Paint | 2.4s | 0.8s | 0.6s |
| Largest Contentful Paint | 3.8s | 1.4s | 0.9s |
| Time to Interactive | 4.2s | 3.1s | 1.1s |
| Hydration süresi | 4.2s | 2.8s | 0.3s |
RSC'nin en büyük etkisi Time to Interactive metriğinde. Geleneksel SSR'da tüm sayfa hydrate edilmesi gerekirken, RSC'de sadece interaktif kısımlar (sepete ekle butonu, filtre menüsü) hydrate ediliyor.
JavaScript Bundle Karşılaştırması
Tipik bir blog sayfasını ele alalım:
Geleneksel yaklaşım (tümü Client Component):
- React runtime: 42 KB
- Blog component'leri: 28 KB
- Markdown renderer: 35 KB
- Syntax highlighter: 45 KB
- Tarih formatlama: 8 KB
- Toplam: ~158 KB
RSC yaklaşımı:
- React runtime (minimal): 12 KB
- Sadece interaktif component'ler: 8 KB
- Toplam: ~20 KB
Fark dramatik. Markdown rendering, syntax highlighting, tarih formatlama gibi işlemler sunucuda yapılıyor ve istemciye sadece HTML gönderiliyor.
Server Actions: Mutation'lar İçin Yeni Paradigma
RSC ile birlikte gelen Server Actions, form gönderimi ve veri değişikliklerini basitleştiriyor.
// Server Action tanımlama
async function createComment(formData: FormData) {
"use server";
const content = formData.get("content") as string;
const postId = formData.get("postId") as string;
// Validasyon
if (!content || content.length < 3) {
return { error: "Yorum en az 3 karakter olmalı" };
}
// Veritabanına kayıt
await db.comment.create({
data: { content, postId, createdAt: new Date() }
});
// Cache'i invalidate et
revalidatePath(`/blog/${postId}`);
return { success: true };
}
// Kullanımı — API route yok, fetch yok
function CommentForm({ postId }: { postId: string }) {
return (
<form action={createComment}>
<input type="hidden" name="postId" value={postId} />
<textarea
name="content"
placeholder="Yorumunuzu yazın..."
required
minLength={3}
/>
<SubmitButton />
</form>
);
}
"use client";
import { useFormStatus } from "react-dom";
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? "Gönderiliyor..." : "Yorum Yap"}
</button>
);
}
Server Actions'ın avantajları:
- API route oluşturmaya gerek yok
- Type-safety: TypeScript ile uçtan uca tip güvenliği
- Progressive enhancement: JavaScript olmadan da çalışır
- Otomatik revalidation:
revalidatePathveyarevalidateTagile cache yönetimi
RSC Kullanım Senaryoları
RSC Kullanın (Server Component)
- Veritabanı sorguları ve API çağrıları
- Markdown/MDX rendering
- Statik içerik gösterimi (ürün açıklaması, blog metni)
- Ağır kütüphaneleri kullanan hesaplamalar (tarih formatlama, veri dönüşümü)
- Sayfa layout'ları ve navigasyon yapıları
Client Component Kullanın
- Form elemanları ve input'lar
- Butonlar ve tıklama etkileşimleri
- Animasyonlar ve geçişler
- Gerçek zamanlı güncellemeler (WebSocket, polling)
- Tarayıcı API'leri (localStorage, geolocation, clipboard)
- Üçüncü parti widget'lar (harita, video oynatıcı)
Hibrit Yaklaşım (Önerilen)
En iyi pratik, sayfayı Server Component olarak tasarlayıp sadece interaktif kısımları Client Component yapmaktır:
// Sayfa — Server Component
async function ProductPage({ params }) {
const product = await getProduct(params.id);
return (
<main>
{/* Statik kısımlar — Server Component */}
<ProductInfo product={product} />
<ProductDescription content={product.description} />
<RelatedProducts categoryId={product.categoryId} />
{/* İnteraktif kısımlar — Client Component */}
<ImageGallery images={product.images} />
<AddToCartForm product={product} />
<ReviewSection productId={product.id} />
</main>
);
}
Migration Rehberi: Mevcut Projeyi RSC'ye Geçirme
Mevcut bir Next.js projesini Pages Router'dan App Router + RSC'ye geçiriyorsanız, adım adım strateji izleyin.
Adım 1: Audit — Hangi Component'ler Server, Hangisi Client?
Her component'i değerlendirin:
useState,useEffect,useRefkullanıyor mu? → Client Component- Event handler'ları var mı (
onClick,onChange)? → Client Component - Tarayıcı API'si kullanıyor mu? → Client Component
- Sadece veri gösteriyor mu? → Server Component yapılabilir
- Ağır kütüphane import ediyor mu? → Server Component'e taşınabilir
Adım 2: Yaprak Node'lardan Başlayın
Component ağacının yaprak node'larından (en alt component'ler) başlayarak "use client" direktifini ekleyin. Üst component'leri Server Component olarak bırakın.
Adım 3: Data Fetching'i Taşıyın
useEffect + useState ile yapılan data fetching'i, Server Component'lere taşıyın:
// ÖNCE — Client Component
"use client";
import { useState, useEffect } from "react";
function BlogList() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("/api/posts")
.then(res => res.json())
.then(data => {
setPosts(data);
setLoading(false);
});
}, []);
if (loading) return <p>Yükleniyor...</p>;
return posts.map(post => <PostCard key={post.id} post={post} />);
}
// SONRA — Server Component
async function BlogList() {
const posts = await db.posts.findMany({
orderBy: { date: "desc" }
});
return posts.map(post => <PostCard key={post.id} post={post} />);
}
Adım 4: API Route'ları Temizleyin
Server Component'ler doğrudan veritabanına erişebildiği için, sadece veri okuma amaçlı API route'ların çoğu gereksiz hale gelir. Mutation'lar için Server Actions kullanın.
Adım 5: Test ve Performans Ölçümü
Migration sonrası mutlaka şunları kontrol edin:
- Lighthouse skoru iyileşti mi?
- JavaScript bundle boyutu azaldı mı?
- Core Web Vitals metrikleri düzeldi mi?
- Tüm interaktif özellikler çalışıyor mu?
Sık Yapılan Hatalar
1. Her yere "use client" eklemek: Bu RSC'nin amacını yok eder. Sadece gerçekten interaktivite gereken component'lere ekleyin.
2. Server Component'ten Client Component'e büyük veri geçmek: Props olarak geçilen veri serialize edilir. Büyük nesneler yerine sadece gerekli alanları geçin.
3. Client Component'i Server Component'in içinde import edememek sanmak: Server Component'ler Client Component'leri import edebilir. Tersi mümkün değil (ama children pattern ile çözülebilir).
4. Caching'i göz ardı etmek: Server Component'lerdeki fetch çağrıları varsayılan olarak cache'lenir. Dinamik veri için { cache: 'no-store' } veya { next: { revalidate: 60 } } kullanın.
Sonuç
React Server Components, web geliştirmede gerçek bir paradigma değişimi. İstemciye gönderilen JavaScript'i dramatik şekilde azaltarak, performansta somut iyileşmeler sağlıyor. Başlangıçta öğrenme eğrisi olsa da, kavrandığında daha temiz, daha performanslı ve daha bakımı kolay uygulamalar geliştirmenizi sağlıyor.
Projenizde RSC'ye geçiş yapmak veya sıfırdan RSC tabanlı bir proje başlatmak istiyorsanız, Maviona'nın web geliştirme hizmetlerine göz atın.
