Maviona
Ücretsiz Teklif Alın
Yazılım

React Server Components Nedir? Pratik Örneklerle Rehber

Eray Yılmaz
Yayın: 15 Şubat 2026Güncellendi: 14 Temmuz 202616 dk
React Server Components Nedir? Pratik Örneklerle Rehber

React Server Components (RSC), bazı component'lerin client bundle'a eklenmeden, istemci uygulamasından ayrı bir ortamda render edilmesini sağlar. React 19'da Server Components modeli kararlıdır; ancak framework ve bundler uygulama API'leri aynı kararlılık garantisine sahip değildir. Bu rehber kavramı Next.js App Router üzerinden açıklar.

React'ın Evrimi: CSR'den RSC'ye

React uygulamalarında bu modeller birbirinin zorunlu devamı değil, birlikte kullanılabilen mimari araçlardır:

  1. Client-Side Rendering (CSR): Arayüz mantığının önemli bölümü tarayıcıda çalışır.
  2. Server-Side Rendering (SSR): İlk HTML sunucuda üretilir; Client Component'ler için hydration yine gerekir.
  3. Streaming SSR: HTML, hazır olan Suspense bölümlerine göre parçalı gönderilebilir.
  4. React Server Components: Server Component modül kodu client bundle'a girmez; ilk yüklemede HTML önizlemesi ve RSC payload, sonraki navigasyonlarda ise RSC payload kullanılabilir.

RSC'nin temel katkısı, sunucuda kalabilecek kod ile interaktif istemci kodu arasında framework tarafından yönetilen bir sınır kurmasıdır. Bu, her uygulamada otomatik olarak daha az JavaScript veya daha iyi Core Web Vitals sonucu vereceği anlamına gelmez.

RSC Nasıl Çalışır?

Temel Kavram

RSC'de component'ler ikiye ayrılır:

Server Components (Next.js App Router'da varsayılan):

  • Build veya istek sırasında server ortamında çalışabilir
  • Kendi modül kodunu client bundle'a eklemez
  • Doğrudan veritabanına, dosya sistemine, API'lere erişebilir
  • useState, useEffect gibi hook'ları kullanamaz
  • Event handler'ları kullanamaz

Client Components ("use client" direktifi):

  • İlk yüklemede server tarafından HTML'e katkı verebilir, ardından tarayıcıda hydrate edilir
  • Interaktivite sağlar
  • State, effect ve event handler kullanabilir
  • Tarayıcı API'lerine erişebilir
// Server Component — varsayılan, "use client" yok
// Bu component'in kendi modül kodu client bundle'a eklenmez
async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params;
  // Doğrudan veritabanı erişimi — API route'a gerek yok
  const product = await db.product.findUnique({
    where: { 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);
  const [error, setError] = useState<string | null>(null);

  async function handleClick() {
    setLoading(true);
    setError(null);

    try {
      const response = await fetch("/api/cart", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ productId }),
      });

      if (!response.ok) throw new Error("Sepet güncellenemedi");
      setAdded(true);
    } catch {
      setError("Sepet güncellenemedi. Lütfen tekrar deneyin.");
    } finally {
      setLoading(false);
    }
  }

  return (
    <>
      <button onClick={handleClick} disabled={loading}>
        {loading ? "Ekleniyor..." : added ? "Eklendi!" : "Sepete Ekle"}
      </button>
      {error && <p role="alert">{error}</p>}
    </>
  );
}

API, productId değerini güvenilir kabul etmemeli; oturum, yetki, stok ve fiyat kontrollerini sunucuda tekrarlamalıdır.

RSC Wire Format

RSC'nin perde arkasında framework tarafından üretilen, serialize edilmiş bir payload vardır. Bu format uygulama kodunun doğrudan bağımlanması gereken kararlı bir genel API değildir. Payload genel olarak:

  • Server Component ağacının render sonucunu
  • Client Component'lerin referanslarını (bundle path) içerir
  • Serialize edilebilen props verilerini
  • Suspense sınırları boyunca stream edilebilen parçaları içerir

Next.js'in Server ve Client Components dokümantasyonuna göre ilk sayfa yüklemesinde HTML hızlı bir önizleme için, RSC payload component ağaçlarını uzlaştırmak için, istemci JavaScript'i de Client Component'leri hydrate etmek için kullanılır. Sonraki navigasyonların akışı farklı olabilir.

Performans Etkisini Nasıl Ölçersiniz?

RSC için evrensel bir bundle veya LCP kazancı yoktur. Sonuç; client boundary'lere, import grafiğine, server gecikmesine, cache politikasına ve kullanıcı cihazına bağlıdır.

Hipotetik Test: E-Ticaret Ürün Listesi

Bu örnek gerçek müşteri vakası değil, kontrollü bir deney taslağıdır. Aynı veri, HTML, stil, görsel ve hosting kullanılarak üç varyant kurulabilir:

Varyant Mimari Beklenti Değil, Ölçülecek Soru
CSR Liste ve veri istemcide İlk render için ne kadar JS ve kaç ağ turu gerekiyor?
SSR + client-heavy İlk HTML server'da, listenin tamamı client bundle'da Hydration süresi ve ana thread maliyeti nedir?
RSC + client islands Liste server'da, filtre ve sepet interaktif Client bundle gerçekten azalıyor mu; server yanıtı nasıl etkileniyor?

Her varyant için production build, aynı cihaz/ağ profili ve birden fazla tekrar kullanın. Transfer edilen ve parse edilen JavaScript'i, LCP/INP/CLS'yi, server TTFB'yi, cache hit durumunu ve hata oranını birlikte raporlayın.

Hipotetik Bundle Örneği

Bir blog sayfasında Markdown parser, syntax highlighter ve tarih formatlama kodu yalnızca server tarafında kullanılıyorsa bu modüllerin tarayıcı bundle'ına girmemesi mümkündür. Aynı kütüphaneleri bir Client Component import ederse bu avantaj kaybolabilir.

Bu nedenle paket boyutunu sabit bir örnek sayıyla varsaymayın. Build analyzer ile hangi modülün hangi client boundary üzerinden bundle'a girdiğini kontrol edin.

Server Functions ve Form Actions

React dokümantasyonu bunları Server Functions olarak adlandırır; bir Server Function formun action prop'una verildiğinde yaygın olarak Server Action denir. Resmî React açıklamasına göre argümanlar istemci tarafından kontrol edilebilir kabul edilmeli; kimlik doğrulama, yetkilendirme ve input validasyonu fonksiyonun içinde yapılmalıdır.

// Server Action tanımlama
async function createComment(formData: FormData) {
  "use server";

  const content = formData.get("content");
  const postId = formData.get("postId");

  if (
    typeof content !== "string" ||
    content.trim().length < 3 ||
    typeof postId !== "string" ||
    !postId
  ) {
    return { error: "Geçersiz yorum verisi" };
  }

  // Kimlik ve yetki kontrolü uygulamaya özel olarak burada yapılmalı.
  const user = await requireUser();
  await assertCanComment(user, postId);

  // Veritabanına kayıt
  await db.comment.create({
    data: { content: content.trim(), 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ı:

  • Yalnızca bu web arayüzünün kullandığı mutation'larda ayrı bir API route ihtiyacını azaltabilir
  • Paylaşılan TypeScript tipleri geliştirme sırasında yardımcı olur; runtime input validasyonunun yerini almaz
  • Server Component'ten forma geçirildiğinde JavaScript yüklenmeden önce form gönderimini destekleyebilir
  • revalidatePath, revalidateTag veya updateTag ile ilgili cache'i açıkça güncelleyebilir

Component Sınırı İçin Başlangıç Noktaları

Server Component Adayları

  • Gizli credential gerektiren veritabanı sorguları ve server API çağrıları
  • Tarayıcıya taşınması gerekmeyen Markdown/MDX rendering
  • Yerel state veya event handler gerektirmeyen içerik gösterimi
  • Server maliyeti ölçülmüş veri dönüşümleri
  • İnteraktif alt parçaları children olarak alabilen layout ve navigasyon kabukları

Client Component Sınırı Gerektirebilen Durumlar

  • Yerel state veya event handler kullanan form davranışı
  • İstemcide çalışması gereken buton ve tıklama etkileşimleri
  • JavaScript kontrollü 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ı)

Salt HTML form alanı, bağlantı veya CSS animasyonu tek başına Client Component gerektirmez. Sınırı kullanılan API ve etkileşim modeli belirler.

Hibrit Yaklaşım

Yaygın bir başlangıç noktası, sayfayı Server Component olarak tasarlayıp yalnızca state, event veya tarayıcı API'si gereken alt ağaçları Client Component yapmaktır. Bu bir kural değil; gerçek bundle ve etkileşim gereksinimiyle doğrulanacak bir tasarım tercihidir:

// Sayfa — Server Component
async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params;
  const product = await getProduct(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, useRef kullanı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: İnteraktif Yapraklardan Başlayın

Component ağacındaki state, event handler veya tarayıcı API'si kullanan en küçük alt ağaçları belirleyin. "use client" direktifini bu sınırın giriş modülüne ekleyin; her yaprak component'i otomatik olarak client'a çevirmeyin.

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 Sınırlarını Gözden Geçirin

Yalnızca aynı Next.js arayüzünün kullandığı okuma işlemleri Server Component'e taşınabilir. Mobil uygulama, partner entegrasyonu, webhook veya kamuya açık istemciler tarafından kullanılan API route'larını koruyun. Mutation'larda Server Action kullanıp kullanmamaya istemci kapsamı, güvenlik ve gözlemlenebilirlik ihtiyacıyla karar verin.

Adım 5: Test ve Performans Ölçümü

Migration sonrası önceki sürümle aynı test koşullarında şunları kontrol edin:

  • Production bundle ve route bazlı istemci JavaScript'i nasıl değişti?
  • Lab ve alan Core Web Vitals dağılımı nasıl değişti?
  • Server TTFB, compute ve cache hit oranı nasıl değişti?
  • Tüm interaktif özellikler çalışıyor mu?
  • Yetkilendirme, hata, loading ve no-JavaScript form akışları ç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 eski varsayımlarla kurmak: Güncel Next.js fetch API'sinde varsayılan davranış auto no cache olarak belgelenir. Cache Components kullanıldığında kalıcı cache'i use cache, cacheLife ve tag'lerle açıkça tanımlayın. Ayrıntılar için Next.js fetch referansına bakın.

Sonuç

React Server Components, server'da kalabilecek modüllerle interaktif istemci kodunu ayırmak için yeni bir component sınırı sunar. Client bundle'ı azaltabilir; buna karşılık server maliyeti, cache tasarımı, serialization ve framework bağımlılığı ekler. Geçiş kararını varsayılan performans vaadiyle değil, production build ve gerçek kullanıcı verisiyle verin.

Kaynaklar ve Yöntem

Kavramsal tanımlar React ve Next.js'in yukarıda bağlanan resmî dokümantasyonundan 14 Temmuz 2026'da kontrol edildi. Hipotetik test gerçek bir Maviona veya müşteri sonucu değildir; örnek sayı içermemesinin nedeni, uygulama ve test bağlamı olmadan bu sayıların genellenememesidir. Kaynaklar Maviona'yı değerlendirmez veya onaylamaz.

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.

Paylaş

Bu Konudaki Hizmetlerimiz

Profesyonel destek almak ister misiniz?

WhatsApp ile yazın