Next.js App Router'da Hata Yönetimi ve Yükleme Durumları: error.tsx, loading.tsx, Suspense ve Streaming Rehberi

Next.js App Router'da error.tsx, loading.tsx, Suspense ve streaming kalıplarını öğrenin. Beklenen ve beklenmeyen hata ayrımı, kurtarma stratejileri ve skeleton bileşenleriyle sağlam uygulamalar geliştirin.

Giriş: Neden Hata Yönetimi ve Yükleme Durumları Bu Kadar Önemli?

Next.js App Router, React Server Components üzerine inşa edilmiş modern bir yönlendirme sistemi. Ama güçlü bir uygulama inşa etmek sadece veri çekmek ve bileşen mimarisiyle sınırlı değil. Kullanıcı deneyimini asıl belirleyen iki kritik faktör var: hatalar oluştuğunda uygulamanın nasıl davrandığı ve içerik yüklenirken kullanıcıya ne gösterildiği.

Araştırmalar gösteriyor ki boş ekranlar, kullanıcıların %80'inin uygulamayı terk etmesine neden oluyor. Düşünsenize — uzun uzun bekliyorsunuz, beyaz bir ekran, hiçbir geri bildirim yok. Doğal olarak kapatırsınız. Öte yandan iyi tasarlanmış yükleme durumları ve hata kurtarma mekanizmaları, kullanıcı güvenini artırır ve uygulamanızı profesyonel bir seviyeye taşır.

Bu rehberde, Next.js App Router'ın dosya tabanlı hata yönetim sistemini, Suspense ile streaming kalıplarını, Server Action'larda beklenen ve beklenmeyen hata ayrımını ve üretim ortamı için en iyi uygulamaları ele alacağız. Hadi başlayalım.

App Router Dosya Hiyerarşisi: Katmanlı Mimari

Next.js App Router, her rota segmenti için katmanlı bir dosya yapısı sunuyor. Bu yapı sayesinde hata ve yükleme durumları otomatik olarak yönetiliyor:

app/
├── layout.tsx        ← Kök düzen
├── loading.tsx       ← Suspense sınırı (sayfa düzeyi)
├── error.tsx         ← Error Boundary (hata sınırı)
├── not-found.tsx     ← 404 geri dönüşü
├── page.tsx          ← Sayfa içeriği
├── global-error.tsx  ← Kök düzen hataları için
└── dashboard/
    ├── layout.tsx
    ├── loading.tsx   ← Dashboard için ayrı yükleme durumu
    ├── error.tsx     ← Dashboard için ayrı hata sınırı
    └── page.tsx

Bu hiyerarşide her dosyanın belirli bir görevi var ve Next.js bunları otomatik olarak React Suspense ve Error Boundary bileşenleriyle sarıyor. Hataların nasıl yukarı doğru kabarcıklandığını (error bubbling) anlamak, doğru yerde doğru dosyayı konumlandırmak için gerçekten kritik.

error.tsx ile Rota Düzeyinde Hata Yönetimi

error.tsx dosyası, bir rota segmentini ve onun alt bileşenlerini otomatik olarak bir React Error Boundary ile sararak çalışma zamanı hatalarını yakalar. Hata oluştuğunda uygulamanın geri kalanı çalışmaya devam eder — yalnızca etkilenen segment için bir geri dönüş arayüzü gösterilir. Bu davranış özellikle büyük uygulamalarda çok değerli.

Temel error.tsx Yapısı

// app/dashboard/error.tsx
'use client' // Zorunlu: Error Boundary bir Client Component olmalıdır

import { useEffect } from 'react'

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  useEffect(() => {
    // Hata raporlama servisine gönder (ör. Sentry)
    console.error('Dashboard hatası:', error)
  }, [error])

  return (
    <div className="error-container">
      <h2>Bir şeyler ters gitti</h2>
      <p>Dashboard yüklenirken bir hata oluştu.</p>
      <button onClick={() => reset()}>
        Tekrar Dene
      </button>
    </div>
  )
}

Dikkat edilmesi gereken noktalar:

  • error.tsx dosyası mutlaka 'use client' direktifi ile işaretlenmeli. Error Boundary'ler yalnızca Client Component olarak çalışabiliyor.
  • error prop'u, üretim ortamında hassas bilgileri gizlemek için temizleniyor. Kullanıcıya yalnızca genel bir mesaj ve sunucu loglarıyla eşleştirmek için kullanılabilecek bir digest özelliği gönderiliyor.
  • reset() fonksiyonu, hata sınırının içeriğini yeniden render etmeyi deniyor — özellikle ağ zaman aşımı gibi geçici hatalar için çok kullanışlı.

Hata Kabarcıklanması (Error Bubbling)

Hatalar, en yakın üst hata sınırına doğru kabarcıklanır. Bu sayede farklı düzeylerde error.tsx dosyaları yerleştirerek ayrıntılı hata yönetimi sağlayabilirsiniz.

Burada kritik bir kural var: Bir error.tsx dosyası, aynı segment'teki layout.tsx veya template.tsx bileşenlerindeki hataları yakalayamaz. Bu hatalar bir üst segment'teki error.tsx tarafından yakalanır. İlk başta kafa karıştırıcı gelebilir ama bu tasarımın güzel bir sebebi var: navigasyon gibi paylaşılan arayüz öğelerinin hata durumunda bile erişilebilir kalmasını sağlıyor.

global-error.tsx: Kök Düzen Hataları İçin Son Çare

Kök app/error.tsx dosyası, app/layout.tsx veya app/template.tsx bileşenlerindeki hataları yakalayamıyor. Bu tür kritik hatalar için app/global-error.tsx dosyası devreye giriyor.

// app/global-error.tsx
'use client'

export default function GlobalError({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  return (
    <html>
      <body>
        <div className="global-error">
          <h1>Kritik bir hata oluştu</h1>
          <p>Uygulama beklenmedik bir sorunla karşılaştı.</p>
          <div>
            <button onClick={() => reset()}>Tekrar Dene</button>
            <a href="/">Ana Sayfaya Dön</a>
          </div>
        </div>
      </body>
    </html>
  )
}

Birkaç önemli detay:

  • global-error.tsx aktif olduğunda kök düzeni tamamen değiştiriyor. Bu yüzden kendi <html> ve <body> etiketlerini içermesi gerekiyor.
  • Kök bileşenler genellikle statik olduğundan bu dosya pratikte nadiren tetikleniyor. Ama üretim ortamında mutlaka bulunması lazım — bir nevi güvenlik ağı.
  • Kök düzende oluşan ölümcül hatalarda reset() sorunu çözemeyebilir. Bu durumda window.location.reload() veya ana sayfaya yönlendirme gibi alternatif kurtarma stratejileri sunmak daha mantıklı.

not-found.tsx: Özel 404 Sayfaları

Next.js, not-found.tsx dosyasıyla eksik içerik senaryoları için özelleştirilmiş 404 sayfaları oluşturmanıza olanak tanıyor. Error boundary'lerden farklı olarak, not-found bileşeni paylaşılan düzen ve navigasyon yapısını koruyor — bu da kullanıcının kaybolmuş hissetmesini engelliyor.

// app/not-found.tsx
import Link from 'next/link'

export default function NotFound() {
  return (
    <div className="not-found">
      <h1>404 — Sayfa Bulunamadı</h1>
      <p>Aradığınız sayfa mevcut değil veya taşınmış olabilir.</p>
      <Link href="/">Ana Sayfaya Dön</Link>
    </div>
  )
}

Programatik olarak 404 tetiklemek istiyorsanız notFound() fonksiyonunu kullanabilirsiniz:

// app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation'

export default async function BlogPost({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  const { slug } = await params
  const post = await getPost(slug)

  if (!post) {
    notFound() // En yakın not-found.tsx'i tetikler
  }

  return <article>{post.content}</article>
}

Dikkat: notFound() bir Suspense sınırı içinde çağrıldığında, arama motorları 404 yerine 200 OK durum kodu görebilir. Kritik 404 kontrollerini mümkün olduğunca Suspense sınırlarının dışında yapmaya özen gösterin.

loading.tsx ile Sayfa Düzeyinde Yükleme Durumları

loading.tsx, React Suspense ile anlamlı yükleme arayüzleri oluşturmanın en kolay yolu. Bu dosya, bir rota segmentinin içeriği sunucudan akışla gelirken kullanıcıya anında bir yükleme durumu gösteriyor.

// app/dashboard/loading.tsx
export default function DashboardLoading() {
  return (
    <div className="dashboard-skeleton">
      <div className="skeleton-header" />
      <div className="skeleton-grid">
        {[1, 2, 3, 4].map((i) => (
          <div key={i} className="skeleton-card" />
        ))}
      </div>
    </div>
  )
}

loading.tsx, aynı klasördeki layout.tsx içine yerleştirilir ve otomatik olarak page.tsx dosyasını bir <Suspense> sınırıyla sarar. Yani siz sadece dosyayı oluşturuyorsunuz, gerisini Next.js hallediyor.

Teknik detaylar:

  • Navigasyon kesilebilir nitelikte — başka bir rotaya geçmek için mevcut rotanın tamamen yüklenmesini beklemenize gerek yok.
  • Varsayılan olarak Server Component, ama "use client" direktifiyle Client Component olarak da kullanılabiliyor.
  • İlk sayfa yüklemesinde de gösteriliyor (sadece istemci tarafı navigasyonda değil).

Suspense ile Bileşen Düzeyinde Granüler Streaming

loading.tsx tüm rota segmenti için tek bir yükleme durumu sunuyor. Peki ya sayfanızda farklı hızlarda yüklenen birden fazla veri kaynağı varsa? İşte burada satır içi Suspense sınırları devreye giriyor ve bileşen düzeyinde kontrol sağlıyor.

Bağımsız Veri Kaynaklarını Paralel Akışla Yükleme

// app/dashboard/page.tsx
import { Suspense } from 'react'
import RevenueChart from './RevenueChart'
import LatestInvoices from './LatestInvoices'
import CardsSkeleton from './CardsSkeleton'
import ChartSkeleton from './ChartSkeleton'
import InvoicesSkeleton from './InvoicesSkeleton'

export default function DashboardPage() {
  return (
    <main>
      <h1>Dashboard</h1>

      {/* Kartlar hızlı yüklenir */}
      <Suspense fallback={<CardsSkeleton />}>
        <DashboardCards />
      </Suspense>

      <div className="grid grid-cols-2 gap-6">
        {/* Grafik ve faturalar bağımsız olarak akışla gelir */}
        <Suspense fallback={<ChartSkeleton />}>
          <RevenueChart />
        </Suspense>

        <Suspense fallback={<InvoicesSkeleton />}>
          <LatestInvoices />
        </Suspense>
      </div>
    </main>
  )
}

Bu yaklaşımda her bileşen bağımsız olarak yükleniyor. Yavaş bir veritabanı sorgusu sayfanın tamamını engellemiyor — diğer bölümler hazır olduğunda anında görünüyor. Açıkçası bu, kullanıcı deneyiminde büyük bir fark yaratıyor.

Suspense Sınırı Yerleştirme Kuralları

  • Suspense sınırı, async veri çeken bileşenin üzerine yerleştirilmeli. Sınırı async bileşenin içine koyarsanız çalışmaz.
  • Bağımsız veri bağımlılığı başına bir Suspense sınırı kullanın. İki veri birlikte yüklenip birlikte gösteriliyorsa tek bir sınır paylaşabilirsiniz.
  • Aynı anda 2-3'ten fazla iskelet yükleyici göstermekten kaçının. Çok fazla sınır, kullanıcı deneyimini bozan "patlamış mısır etkisi" (popcorn effect) yaratır — her şey farklı zamanlarda "pop" eder ve sayfa sürekli kayar.

Skeleton Bileşenlerinde Boyut Eşleştirme

İskelet bileşenleri, son içeriğin boyutlarıyla eşleşen açık boyutlar belirlemeli. Eşleşmeyen boyutlar, her Suspense sınırı çözüldüğünde Cumulative Layout Shift (CLS) puanınızı olumsuz etkiler ve titrek bir kullanıcı deneyimi oluşturur.

// components/ChartSkeleton.tsx
export default function ChartSkeleton() {
  return (
    <div
      className="animate-pulse bg-gray-200 rounded-xl"
      style={{ width: '100%', height: '350px' }} // Gerçek grafik boyutuyla eşleşmeli
    />
  )
}

Streaming: Sunucudan İstemciye Aşamalı İçerik Aktarımı

Streaming, bir rotayı daha küçük parçalara bölerek hazır oldukça sunucudan istemciye aşamalı olarak aktarmanızı sağlayan bir teknik. Geleneksel SSR'da sunucu tüm veriyi toplar, tüm HTML'i oluşturur ve tek seferde gönderir. Streaming ile bu sıralı ve engelleyici süreç ortadan kalkıyor.

Streaming'in Performans Etkisi

  • Time To First Byte (TTFB): Azalır, çünkü sunucu ilk parçayı hemen göndermeye başlıyor.
  • First Contentful Paint (FCP): İyileşir, kullanıcı anında bir iskelet veya başlık görüyor.
  • Time to Interactive (TTI): Özellikle yavaş cihazlarda belirgin şekilde iyileşiyor.

Kısacası, streaming kullanmak kullanıcının "bir şeyler oluyor" hissini çok daha erken yaşamasını sağlıyor.

Partial Prerendering (PPR) ile Streaming

Next.js 15 ile tanıtılan Partial Prerendering (PPR), statik bir kabuğu derleme zamanında önceden oluşturuyor ve CDN hızında sunuyor. Suspense sınırlarıyla sarılmış dinamik içerik ise kabuk ulaştıktan sonra akışla geliyor.

// next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  experimental: {
    ppr: true, // Partial Prerendering'i etkinleştir
  },
}

export default nextConfig

PPR ile zihinsel model oldukça basit: "Herhangi bir veritabanı sorgusu tamamlanmadan önce kullanıcıya ne gösterebilirim?" Bu içerik statik kabuğa gidiyor. Geri kalan her şey bir Suspense sınırı içinde dinamik ada oluyor. Bence bu yaklaşım, performans optimizasyonunu düşünme biçimimizi kökten değiştiren bir paradigma.

Server Action'larda Hata Yönetimi: Beklenen ve Beklenmeyen Hatalar

Next.js, Server Action'larda iki farklı hata türünü birbirinden net şekilde ayırıyor. Bu ayrımı anlamak hem doğru kullanıcı deneyimi hem de güvenlik açısından kritik.

Beklenen Hatalar: useActionState ile Dönüş Değeri Olarak

Form doğrulama hataları veya iş kuralı ihlalleri gibi beklenen hatalar fırlatılmamalı, dönüş değeri olarak döndürülmeli. Bu hatalar için try/catch yerine useActionState hook'unu kullanıyoruz.

// app/actions/user.ts
'use server'

import { z } from 'zod'

const UserSchema = z.object({
  name: z.string().min(2, 'İsim en az 2 karakter olmalıdır'),
  email: z.string().email('Geçerli bir e-posta adresi girin'),
})

export type FormState = {
  errors?: {
    name?: string[]
    email?: string[]
  }
  message?: string
}

export async function createUser(
  prevState: FormState,
  formData: FormData
): Promise<FormState> {
  const validatedFields = UserSchema.safeParse({
    name: formData.get('name'),
    email: formData.get('email'),
  })

  if (!validatedFields.success) {
    return {
      errors: validatedFields.error.flatten().fieldErrors,
    }
  }

  // Veritabanı işlemi...
  return { message: 'Kullanıcı başarıyla oluşturuldu' }
}
// app/components/UserForm.tsx
'use client'

import { useActionState } from 'react'
import { createUser, type FormState } from '../actions/user'

const initialState: FormState = {}

export default function UserForm() {
  const [state, formAction, isPending] = useActionState(
    createUser,
    initialState
  )

  return (
    <form action={formAction}>
      <div>
        <label htmlFor="name">İsim</label>
        <input id="name" name="name" required />
        {state.errors?.name && (
          <p className="text-red-500">{state.errors.name[0]}</p>
        )}
      </div>
      <div>
        <label htmlFor="email">E-posta</label>
        <input id="email" name="email" type="email" required />
        {state.errors?.email && (
          <p className="text-red-500">{state.errors.email[0]}</p>
        )}
      </div>
      <button type="submit" disabled={isPending}>
        {isPending ? 'Gönderiliyor...' : 'Oluştur'}
      </button>
      {state.message && (
        <p className="text-green-500">{state.message}</p>
      )}
    </form>
  )
}

Beklenmeyen Hatalar: Error Boundary'ye Fırlatma

Veritabanı çökmeleri veya ağ arızaları gibi beklenmeyen hatalar ise fırlatılmalı. Bu hatalar en yakın error.tsx tarafından yakalanır.

// app/actions/data.ts
'use server'

export async function fetchCriticalData() {
  const res = await fetch('https://api.example.com/data')

  if (!res.ok) {
    // Bu hata en yakın error.tsx tarafından yakalanır
    throw new Error('Veri alınamadı')
  }

  return res.json()
}

Önemli bir tuzak: redirect() fonksiyonu dahili olarak bir hata fırlatarak çalışıyor. Bu yüzden try/catch bloğunun dışında çağrılması gerekiyor — aksi takdirde catch bloğu onu yakalayıp yutacaktır.

// Doğru kullanım
'use server'

import { redirect } from 'next/navigation'

export async function createPost(formData: FormData) {
  let postId: string

  try {
    postId = await savePost(formData)
  } catch (error) {
    throw new Error('Gönderi kaydedilemedi')
  }

  // redirect try/catch DIŞINDA çağrılmalı
  redirect(`/posts/${postId}`)
}

Kurtarma Kalıpları: reset() ve Ötesi

Etkili hata yönetimi yalnızca hataları yakalamakla kalmaz, kullanıcıya kurtarma yolları da sunar. reset() fonksiyonu en temel kurtarma mekanizması, ama farklı senaryolar farklı stratejiler gerektiriyor.

Geçici Hatalar İçin Yeniden Deneme Mekanizması

// app/dashboard/error.tsx
'use client'

import { useEffect, useState } from 'react'

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  const [retryCount, setRetryCount] = useState(0)
  const MAX_RETRIES = 3

  useEffect(() => {
    console.error(error)
  }, [error])

  const handleRetry = () => {
    if (retryCount < MAX_RETRIES) {
      setRetryCount((prev) => prev + 1)
      reset()
    }
  }

  return (
    <div className="error-container">
      <h2>Bir hata oluştu</h2>
      {retryCount < MAX_RETRIES ? (
        <button onClick={handleRetry}>
          Tekrar Dene ({MAX_RETRIES - retryCount} deneme kaldı)
        </button>
      ) : (
        <div>
          <p>Sorun devam ediyor. Lütfen daha sonra tekrar deneyin.</p>
          <a href="/">Ana Sayfaya Dön</a>
        </div>
      )}
    </div>
  )
}

Farklı Hata Türlerine Göre Kurtarma Stratejileri

Hata Türü Kurtarma Stratejisi
Ağ zaman aşımı reset() ile yeniden deneme
Kimlik doğrulama hatası Giriş sayfasına yönlendirme
Kök düzen hatası window.location.reload() veya ana sayfaya bağlantı
Form doğrulama hatası useActionState ile satır içi hata mesajları
API hız sınırı Bekleme süresi ile geciktirilmiş yeniden deneme

useSearchParams ve Suspense: Yaygın Bir Tuzak

useSearchParams() hook'unu bir Suspense sınırı olmadan kullanmak, tüm sayfayı istemci tarafı render'a (CSR) zorluyor. Sonuç? Sayfa, istemci JavaScript'i yüklenene kadar bomboş kalıyor. Bu hatayı kendi projelerimde de yaptım ve fark etmesi kolay değil.

// ❌ Yanlış: Tüm sayfa CSR'a düşer
'use client'
import { useSearchParams } from 'next/navigation'

export default function SearchPage() {
  const searchParams = useSearchParams()
  // ...
}

// ✅ Doğru: useSearchParams kullanan bileşeni Suspense ile sarın
import { Suspense } from 'react'
import SearchResults from './SearchResults'

export default function SearchPage() {
  return (
    <main>
      <h1>Arama</h1>
      <Suspense fallback={<div>Yükleniyor...</div>}>
        <SearchResults />
      </Suspense>
    </main>
  )
}

Bu yaklaşım, statik kabuğu koruyor ve yalnızca arama parametrelerine bağımlı bileşeni istemci tarafında render ediyor.

Üretim Ortamı İçin En İyi Uygulamalar

Şimdi gelelim işin pratik tarafına. Aşağıdaki kontrol listesi, üretim ortamında sağlam hata yönetimi ve yükleme durumları için temel uygulamaları içeriyor:

  1. Her zaman global-error.tsx tanımlayın: Kök düzey hataları yakalamak için mutlaka bulunmalı.
  2. global-error.tsx ile error.tsx'yi karıştırmayın: Her ikisi de gerekli. error.tsx kök düzen içindeki hataları, global-error.tsx ise kök düzen hatalarını yakalıyor.
  3. Hassas bilgileri sızdırmayın: Üretim ortamında hata mesajları kullanıcıya genel olmalı, detaylı bilgiler sunucu loglarına yazılmalı.
  4. İskelet boyutlarını eşleştirin: Skeleton bileşenleri gerçek içerikle aynı boyutlarda olmalı — aksi halde CLS puanınız düşer.
  5. Suspense sınırlarını aşırı kullanmayın: Aynı anda 2-3'ten fazla iskelet yükleyici göstermek, "patlamış mısır etkisi" oluşturur.
  6. Hata raporlama servisi entegre edin: error.tsx içindeki useEffect ile hataları Sentry veya LogRocket gibi servislere gönderin.
  7. Performansı ölçün: TTFB, LCP ve CLS metriklerini gerçek kullanıcı izleme (RUM) ile doğrulayın.

Tam Uygulama Örneği: Dashboard ile Hata ve Yükleme Yönetimi

Şimdiye kadar anlattığımız tüm kalıpları bir arada görelim. Aşağıda, gerçek bir dashboard uygulamasının dosya yapısı ve ana sayfa bileşeni var:

// Dosya yapısı
app/
├── layout.tsx
├── error.tsx           ← Kök hata sınırı
├── global-error.tsx    ← Kök düzen hataları
├── not-found.tsx       ← Genel 404
└── dashboard/
    ├── layout.tsx
    ├── loading.tsx     ← Dashboard iskelet
    ├── error.tsx       ← Dashboard hata sınırı
    ├── page.tsx        ← Ana dashboard sayfası
    └── invoices/
        ├── loading.tsx ← Faturalar iskelet
        ├── error.tsx   ← Faturalar hata sınırı
        └── page.tsx
// app/dashboard/page.tsx
import { Suspense } from 'react'
import RevenueChart from '@/components/RevenueChart'
import LatestInvoices from '@/components/LatestInvoices'
import StatCards from '@/components/StatCards'
import {
  CardsSkeleton,
  ChartSkeleton,
  InvoicesSkeleton,
} from '@/components/Skeletons'

export default function DashboardPage() {
  return (
    <main className="p-6">
      <h1 className="text-2xl font-bold mb-6">Dashboard</h1>

      <Suspense fallback={<CardsSkeleton />}>
        <StatCards />
      </Suspense>

      <div className="grid grid-cols-1 md:grid-cols-2 gap-6 mt-6">
        <Suspense fallback={<ChartSkeleton />}>
          <RevenueChart />
        </Suspense>

        <Suspense fallback={<InvoicesSkeleton />}>
          <LatestInvoices />
        </Suspense>
      </div>
    </main>
  )
}

Sıkça Sorulan Sorular (SSS)

Next.js App Router'da error.tsx neden Client Component olmak zorunda?

React Error Boundary'ler istemci tarafında çalışan bir mekanizma. Bileşen ağacındaki çalışma zamanı hatalarını yakalamak, geri dönüş arayüzü göstermek ve reset() gibi etkileşimli kurtarma fonksiyonlarını çalıştırmak için tarayıcıda render edilmeleri gerekiyor. Bu yüzden error.tsx dosyası zorunlu olarak 'use client' direktifine sahip olmalı.

loading.tsx ile Suspense arasındaki fark nedir, hangisini ne zaman kullanmalıyım?

loading.tsx, tüm rota segmenti için otomatik olarak bir Suspense sınırı oluşturan dosya tabanlı bir kısayol. Basit sayfalar için ideal. Satır içi <Suspense> ise bileşen düzeyinde granüler kontrol sağlıyor ve farklı hızlarda yüklenen birden fazla bağımsız veri kaynağı olduğunda tercih edilmeli.

İkisini birlikte de kullanabilirsiniz: loading.tsx tüm sayfa için bir temel yükleme durumu sağlarken, sayfa içinde belirli bileşenler kendi Suspense sınırlarıyla daha ince kontrol elde edebilir.

Server Component'lerde try/catch kullanmalı mıyım yoksa error.tsx'e mi bırakmalıyım?

Bu, hatanın türüne bağlı. Beklenen hatalar (form doğrulama, iş kuralı ihlalleri) için try/catch kullanın ve hata mesajını dönüş değeri olarak döndürün. Beklenmeyen hatalar (veritabanı çökmeleri, ağ arızaları) için hatayı fırlatarak error.tsx'nin yakalamasına izin verin.

Ve redirect() fonksiyonunu try/catch bloğunun dışında çağırmayı unutmayın — çünkü redirect dahili olarak bir hata fırlatıyor.

Suspense sınırı içinde notFound() çağırmak neden sorunlu olabilir?

Bir Suspense sınırı içinde notFound() çağrıldığında, React sunucu render'ını durdurmaz ve önce Suspense fallback'ini gönderir. Bu durumda HTTP yanıtı zaten 200 OK olarak başlamış olur. Sonuç olarak arama motorları sayfayı 404 yerine 200 olarak indeksleyebilir. Bunu önlemek için kritik varlık kontrollerini Suspense sınırlarının dışında yapın veya generateMetadata içinde kontrol edip notFound() çağırın.

Streaming ve Suspense Core Web Vitals'ı nasıl etkiler?

Streaming, TTFB ve FCP metriklerini iyileştiriyor çünkü sunucu ilk HTML parçasını hemen gönderiyor. Ancak dikkatli kullanılmazsa CLS puanını olumsuz etkileyebilir — bu yüzden iskelet bileşenlerinizin gerçek içerikle aynı boyutlarda olduğundan emin olun. TTI da özellikle yavaş cihazlarda iyileşiyor çünkü React bileşenleri akışla geldikçe aşamalı olarak hydrate ediyor.

Yazar Hakkında Editorial Team

Our team of expert writers and editors.