# React 19: Produksiyonda Server Components - Eksiksiz Rehber > React 19 Server Components ile produksiyon ortaminda ustun performans. Mimari, kaliplar, streaming, onbellekleme ve optimizasyon teknikleri. - Published: 2026-01-08 - Updated: 2026-04-06 - Author: SharpSkill - Tags: react 19, server components, rsc, performance, next.js - Reading time: 14 min --- Server Components, Hooks'tan bu yana React'taki en onemli gelisimi temsil etmektedir. React 19 ile bu mimari olgunlasarak produksiyona hazir hale gelmistir; bilesenlerin dogrudan sunucuda calismasini saglarken istemci tarafindaki etkilesimi de korumaktadir. > **On kosullar** > > Bu rehber, React ve Next.js App Router hakkinda temel bilgi sahibi olunmasini varsaymaktadir. Ornekler, React Server Components destegini yerel olarak sunan Next.js 14+ kullanmaktadir. ## Understanding the Server Components Architecture Server Components (RSC) yeni bir paradigma sunmaktadir: bazi bilesenler yalnizca sunucuda, digerleriyse istemcide calisir ve her iki tur ayni bilesen agacinda bir arada bulunabilir. Bu ayrim, tarayiciya gonderilen JavaScript paketini azaltarak performansi onemli olcude optimize etmektedir. Temel fikir, bircok bilesenin etkilesime ihtiyac duymadigi gercegine dayanmaktadir. Veritabanindan bir makale listesi goruntuleyen bir bilesen, tamamen sunucu tarafinda calisabilir. Yalnizca etkilesimli ogeler (butonlar, formlar, animasyonlar) istemci tarafinda JavaScript gerektirir. ```tsx // app/articles/page.tsx // This component runs only on the server // No JavaScript is sent to the client for this component import { getArticles } from '@/lib/articles' import ArticleCard from './ArticleCard' import LikeButton from './LikeButton' // async/await directly in the component // Only possible with Server Components export default async function ArticlesPage() { // Direct database call (no REST API needed) const articles = await getArticles() return (

Recent Articles

{articles.map((article) => ( // ArticleCard is also a Server Component {/* LikeButton is a Client Component (interactive) */} ))}
) } ``` `"use client"` direktifi, tarayici JavaScript'i gerektiren bilesenleri acikca isaretlemektedir. ```tsx // app/articles/LikeButton.tsx 'use client' // useState and interactive hooks require "use client" import { useState, useTransition } from 'react' import { likeArticle } from '@/actions/articles' interface LikeButtonProps { articleId: string initialLikes?: number } export default function LikeButton({ articleId, initialLikes = 0 }: LikeButtonProps) { // Local state for optimistic UI const [likes, setLikes] = useState(initialLikes) const [isPending, startTransition] = useTransition() const handleLike = () => { // Immediate optimistic update setLikes((prev) => prev + 1) // Server Action to persist startTransition(async () => { await likeArticle(articleId) }) } return ( ) } ``` Bu mimari, JavaScript paket boyutunu onemli olcude azaltmaktadir: istemciye yalnizca `LikeButton` kodu gonderilir, `ArticlesPage` veya `ArticleCard` degil. ## Server/Client Composition Patterns Server Components ile Client Components arasindaki kompozisyon belirli kurallara tabidir. Bir Server Component, Client Components'i ice aktarabilir ve render edebilir; ancak bunun tersi dogrudan mumkun degildir. Sunucu icerigini bir istemci bilesenine aktarmak icin `children` kalibindan yararlanilmaktadir. ```tsx // components/InteractiveWrapper.tsx 'use client' import { useState, ReactNode } from 'react' interface InteractiveWrapperProps { children: ReactNode expandable?: boolean } // Client Component that wraps server content export function InteractiveWrapper({ children, expandable = false }: InteractiveWrapperProps) { const [isExpanded, setIsExpanded] = useState(!expandable) if (!expandable) { return
{children}
} return (
{isExpanded && (
{/* children can contain Server Components */} {children}
)}
) } ``` ```tsx // app/dashboard/page.tsx // Server Component using the client wrapper import { InteractiveWrapper } from '@/components/InteractiveWrapper' import { getStats, getRecentActivity } from '@/lib/dashboard' export default async function DashboardPage() { // Parallel server-side requests const [stats, activity] = await Promise.all([ getStats(), getRecentActivity() ]) return (
{/* Stats in an expandable wrapper */} {/* This content is rendered server-side then passed to client */}
{/* Recent activity */}
) } ``` Bu kalip, mantigi cogaltmadan istemci etkilesimini sunucu tarafinda render edilen verilerle birlestirmeyi mumkun kilmaktadir. ## Data Fetching and Caching > **React tarafindan genisletilen fetch** > > React 19, yerel `fetch` API'sini otomatik olarak genisleterek tekillesirme ve onbellekleme ozellikleri eklemektedir. Ayni render icindeki ozdes istekler yalnizca bir kez calistirilir. Server Components'te veri cekme islemi dogrudan `async/await` ile gerceklestirilmektedir. React, ozdes isteklerin tekillestirilmesini otomatik olarak yonetir. ```tsx // lib/api.ts // Centralized request configuration with caching const API_BASE = process.env.API_URL // Request with time-based revalidation export async function getProducts() { const response = await fetch(`${API_BASE}/products`, { // Revalidate every hour next: { revalidate: 3600 } }) if (!response.ok) { throw new Error('Failed to fetch products') } return response.json() } // Request without cache (real-time data) export async function getCurrentUser() { const response = await fetch(`${API_BASE}/me`, { // No cache, always fresh cache: 'no-store' }) if (!response.ok) { return null } return response.json() } // Request with tag for targeted invalidation export async function getProduct(id: string) { const response = await fetch(`${API_BASE}/products/${id}`, { next: { tags: [`product-${id}`], revalidate: 3600 } }) if (!response.ok) { throw new Error('Product not found') } return response.json() } ``` Dogrudan veritabani erisimi (Prisma, Drizzle) icin React cache ile `unstable_cache` ayni yetenekleri sunmaktadir. ```tsx // lib/db-queries.ts import { unstable_cache } from 'next/cache' import { prisma } from '@/lib/prisma' // Cache categories (rarely modified) export const getCategories = unstable_cache( async () => { return prisma.category.findMany({ orderBy: { name: 'asc' } }) }, ['categories'], // Cache key { revalidate: 86400, // 24 hours tags: ['categories'] } ) // Cache products by category export const getProductsByCategory = unstable_cache( async (categoryId: string) => { return prisma.product.findMany({ where: { categoryId }, include: { images: true }, orderBy: { createdAt: 'desc' } }) }, ['products-by-category'], { revalidate: 3600, tags: ['products'] } ) // Cache invalidation after mutation export async function createProduct(data: ProductInput) { const product = await prisma.product.create({ data }) // Invalidate related caches revalidateTag('products') return product } ``` ## Streaming and Suspense for Optimal UX Streaming, HTML'in tarayiciya asama asama gonderilmesini saglayarak mevcut bolumlerin aninda goruntulenmesine olanak tanirken diger bolumler yuklenmektedir. Suspense ile birlestirildiginde bu mekanizma, Time to First Byte (TTFB) degerini ve algilanan kullanici deneyimini buyuk olcude iyilestirmektedir. ```tsx // app/product/[id]/page.tsx import { Suspense } from 'react' import { getProduct } from '@/lib/products' import ProductDetails from './ProductDetails' import ProductReviews from './ProductReviews' import RecommendedProducts from './RecommendedProducts' import { Skeleton } from '@/components/ui/Skeleton' interface ProductPageProps { params: { id: string } } export default async function ProductPage({ params }: ProductPageProps) { // This request blocks initial render const product = await getProduct(params.id) return (
{/* Immediate render with product data */} {/* Reviews load via streaming */}

Customer Reviews

}> {/* This async component will be streamed */}
{/* Recommendations too */}

Similar Products

}>
) } // Skeleton for reviews function ReviewsSkeleton() { return (
{[1, 2, 3].map((i) => (
))}
) } ``` ```tsx // app/product/[id]/ProductReviews.tsx // Async Server Component that will be streamed import { getProductReviews } from '@/lib/reviews' interface ProductReviewsProps { productId: string } export default async function ProductReviews({ productId }: ProductReviewsProps) { // This request may take time // Component will be streamed when complete const reviews = await getProductReviews(productId) if (reviews.length === 0) { return (

No reviews yet. Be the first to share your thoughts!

) } return (
{reviews.map((review) => (
{review.author} {'★'.repeat(review.rating)}{'☆'.repeat(5 - review.rating)}

{review.content}

))}
) } ``` Tarayici, once iskelet yapilarini iceren HTML kabuğunu alir, ardindan gercek icerik streaming araciligiyla asama asama enjekte edilir. ## Server Actions for Mutations Server Actions, API rotalari olusturmaya gerek kalmadan istemci bilesenlerinden sunucu kodunun calistirilmasini saglamaktadir. Bu yaklasim, mutasyon yonetimini onemli olcude basitlestirmektedir. ```tsx // actions/cart.ts 'use server' import { revalidatePath } from 'next/cache' import { cookies } from 'next/headers' import { prisma } from '@/lib/prisma' import { getCurrentUser } from '@/lib/auth' // Action to add to cart export async function addToCart(productId: string, quantity: number = 1) { const user = await getCurrentUser() if (!user) { // Return structured error return { error: 'Login required', code: 'UNAUTHORIZED' } } try { // Check stock const product = await prisma.product.findUnique({ where: { id: productId } }) if (!product || product.stock < quantity) { return { error: 'Insufficient stock', code: 'OUT_OF_STOCK' } } // Add or update cart item await prisma.cartItem.upsert({ where: { cartId_productId: { cartId: user.cartId, productId } }, update: { quantity: { increment: quantity } }, create: { cartId: user.cartId, productId, quantity } }) // Invalidate cart page cache revalidatePath('/cart') return { success: true, message: 'Product added to cart' } } catch (error) { console.error('Add to cart error:', error) return { error: 'An error occurred', code: 'SERVER_ERROR' } } } // Action to remove from cart export async function removeFromCart(itemId: string) { const user = await getCurrentUser() if (!user) { return { error: 'Login required' } } await prisma.cartItem.delete({ where: { id: itemId, cart: { userId: user.id } } }) revalidatePath('/cart') return { success: true } } ``` ```tsx // components/AddToCartButton.tsx 'use client' import { useTransition } from 'react' import { addToCart } from '@/actions/cart' import { toast } from '@/components/ui/toast' interface AddToCartButtonProps { productId: string } export function AddToCartButton({ productId }: AddToCartButtonProps) { const [isPending, startTransition] = useTransition() const handleClick = () => { startTransition(async () => { const result = await addToCart(productId) if (result.error) { toast.error(result.error) return } toast.success(result.message) }) } return ( ) } ``` > **Sunucu tarafinda dogrulama** > > Server Actions'ta veriler her zaman sunucu tarafinda dogrulanmalidir. Istemci tarafindaki dogrulamalar atlanabilir. Saglam dogrulama icin Zod veya benzer bir kutuphane kullanilmalidir. ## Error Handling and Error Boundaries React 19, Server Components'te hata yonetimini iyilestirmektedir. Error Boundaries, Client Components'tekiyle ayni sekilde calismaktadir. ```tsx // app/products/error.tsx 'use client' // Error Boundary for /products segment interface ErrorProps { error: Error & { digest?: string } reset: () => void } export default function ProductsError({ error, reset }: ErrorProps) { return (

Loading Error

Unable to load products. Please try again.

{/* Display digest for debugging */} {error.digest && (

Reference: {error.digest}

)}
) } ``` ```tsx // app/products/loading.tsx // Loading UI during initial load export default function ProductsLoading() { return (
{[1, 2, 3, 4, 5, 6].map((i) => (
))}
) } ``` Belirli bilesenlerde daha ayrintili hata yonetimi icin `ErrorBoundary` bileseni dogrudan kullanilabilmektedir. ```tsx // components/ErrorBoundary.tsx 'use client' import { Component, ReactNode } from 'react' interface Props { children: ReactNode fallback?: ReactNode } interface State { hasError: boolean error?: Error } export class ErrorBoundary extends Component { constructor(props: Props) { super(props) this.state = { hasError: false } } static getDerivedStateFromError(error: Error): State { return { hasError: true, error } } componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { // Log error to monitoring service console.error('ErrorBoundary caught:', error, errorInfo) } render() { if (this.state.hasError) { return this.props.fallback || (

An error occurred

) } return this.props.children } } ``` ## Production Performance Optimizations Produksiyon ortaminda Server Components performansini optimize etmek icin cesitli teknikler uygulanabilmektedir. ```tsx // app/layout.tsx import { Suspense } from 'react' import { headers } from 'next/headers' // Preload critical data export const dynamic = 'force-dynamic' export default async function RootLayout({ children }: { children: React.ReactNode }) { return ( {/* Header streamed independently */} }>
{children}
{/* Footer can be static */}