REACT / NEXT.JS // NEXT.JSD::04 İLERİ
25m READCOMPLETION: 91%ID::RCT-101

NEXT.JS SERVER COMPONENTS DERİN DIVE

RSC mimarisi, Suspense boundaries ve streaming SSR

Next.js 13 ile hayatımıza giren App Router ve React Server Components (RSC), frontend mimarisini kökten değiştirdi. Artık bileşenler varsayılan olarak sunucuda render edilir; JavaScript bundle'ı küçülür, veri akışı kolaylaşır ve SEO doğal olarak çalışır.

Server Components vs Client Components

ÖzellikServer ComponentClient Component
Render yeriSunucuTarayıcı
Bundle'a girer mi?HayırEvet
useState / useEffect?HayırEvet
Doğrudan DB erişimi?EvetHayır
Varsayılan mı?EvetHayır (gerekiyor 'use client')
// TSX //
// app/posts/page.tsx — Server Component (varsayılan)
import { db } from '@/lib/db';
 
export default async function PostsPage() {
  // Doğrudan veritabanı sorgusu — istemciye sızmaz
  const posts = await db.post.findMany({ orderBy: { createdAt: 'desc' } });
 
  return (
    <ul>
      {posts.map(post => <PostCard key={post.id} post={post} />)}
    </ul>
  );
}

Veri Akışı: Fetch ve Cache

// TSX //
// Veri alma — React.cache ile tekil istek
import { cache } from 'react';
 
export const getPost = cache(async (slug: string) => {
  const res = await fetch(`https://api.example.com/posts/${slug}`, {
    next: { revalidate: 3600 }, // 1 saatte bir yenile
  });
  if (!res.ok) throw new Error('Post bulunamadı');
  return res.json();
});
 
// page.tsx ve layout.tsx aynı slug ile çağırsa bile tek istek gider

generateStaticParams ile SSG

// TSX //
// app/posts/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await db.post.findMany({ select: { slug: true } });
  return posts.map(p => ({ slug: p.slug }));
}
 
export async function generateMetadata({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug);
  return {
    title: post.title,
    description: post.excerpt,
    openGraph: { images: [post.ogImage] },
  };
}
 
export default async function PostPage({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug);
  return <article>{/* ... */}</article>;
}

Suspense ve Streaming

Server Components, React Suspense ile birleşince Streaming SSR'ı mümkün kılar — ağır veri yükleyen bileşenler beklenirken sayfanın geri kalanı tarayıcıya akar.

// TSX //
import { Suspense } from 'react';
 
export default function DashboardPage() {
  return (
    <div>
      <h1>Dashboard</h1>
 
      {/* Hızlı yüklenen kısım hemen görünür */}
      <QuickStats />
 
      {/* Ağır sorgu için skeleton göster */}
      <Suspense fallback={<ChartSkeleton />}>
        <RevenueChart />  {/* async server component */}
      </Suspense>
 
      <Suspense fallback={<TableSkeleton />}>
        <RecentOrders />  {/* async server component */}
      </Suspense>
    </div>
  );
}

Client Island Deseni

Etkileşimli bileşenler mümkün olan en küçük birimde 'use client' almalıdır:

// TSX //
// components/LikeButton.tsx
'use client';
 
import { useState, useTransition } from 'react';
import { likePost } from '@/actions/post.actions';
 
export default function LikeButton({ postId, initialCount }: { postId: string; initialCount: number }) {
  const [count, setCount] = useState(initialCount);
  const [isPending, startTransition] = useTransition();
 
  function handleClick() {
    startTransition(async () => {
      await likePost(postId);
      setCount(c => c + 1);
    });
  }
 
  return (
    <button onClick={handleClick} disabled={isPending}>
{count}
    </button>
  );
}
 
// Server Component içinde kullan
import LikeButton from '@/components/LikeButton';
 
export default async function PostPage({ params }) {
  const post = await getPost(params.slug);
  return (
    <article>
      <h1>{post.title}</h1>
      {/* Sadece bu küçük bileşen client bundle'a girer */}
      <LikeButton postId={post.id} initialCount={post.likeCount} />
    </article>
  );
}

Server Actions

React 19 ve Next.js 15+ ile form gönderimi için ayrı API route yazmak zorunda değilsiniz:

// TSX //
// actions/post.actions.ts
'use server';
 
import { z } from 'zod';
import { revalidatePath } from 'next/cache';
import { db } from '@/lib/db';
import { getServerSession } from '@/lib/auth';
 
const CreatePostSchema = z.object({
  title:   z.string().min(5).max(200),
  content: z.string().min(50),
});
 
export async function createPost(formData: FormData) {
  const session = await getServerSession();
  if (!session) throw new Error('Giriş yapınız.');
 
  const data = CreatePostSchema.parse({
    title:   formData.get('title'),
    content: formData.get('content'),
  });
 
  await db.post.create({
    data: { ...data, authorId: session.user.id },
  });
 
  revalidatePath('/posts');
}

Sonuç

React Server Components ve Next.js App Router, web geliştirmede paradigma kaymasını temsil eder. Sunucu tarafında veri çekimi, otomatik bundle optimizasyonu ve Streaming SSR ile kullanıcı deneyimi ve geliştirici ergonomisi aynı anda iyileşir. Bir sonraki konuda Parallel Routes ve Intercepting Routes mimarisini inceleyeceğiz.