Ders 07: SSR ve Rendering Modes
🎯 Bu Derste Neleri Öğreneceksiniz?
Section titled “🎯 Bu Derste Neleri Öğreneceksiniz?”- SSR (Server-Side Rendering) nedir ve neden kullanmalıyız?
- Selective SSR ile route bazlı kontrol
- SPA mode tam olarak nedir?
- Static prerendering nasıl yapılır?
- Streaming SSR ile performans optimizasyonu
- Her rendering modu ne zaman kullanılmalı?
📚 Rendering Modlarına Giriş
Section titled “📚 Rendering Modlarına Giriş”TanStack Start, benzersiz bir şekilde birden fazla rendering modunu aynı uygulamada destekler. Bu sayede her route için en uygun rendering stratejisini seçebilirsiniz.
Rendering Modları Karşılaştırması
Section titled “Rendering Modları Karşılaştırması”| Rendering Modu | Açıklama | SEO | İlk Yükleme | Kullanım Durumu |
|---|---|---|---|---|
| SSR | Sunucuda HTML oluşturulur | ✅ Mükemmel | 🟡 Orta | Dinamik içerik, SEO kritik |
| SPA Mode | Tamamen client-side rendering | ❌ Yok | 🟡 Orta | Admin panel, dashboard |
| Static Prerendering | Build zamanında HTML oluşturulur | ✅ Mükemmel | ✅ Hızlı | Statik içerik, blog |
| Selective SSR | Route bazlı seçim | ✅ Özelleştirilebilir | ✅ Özelleştirilebilir | Hibrit uygulamalar |
🌐 SSR (Server-Side Rendering)
Section titled “🌐 SSR (Server-Side Rendering)”SSR, sayfanın HTML’inin sunucuda oluşturulup tarayıcıya gönderilmesi işlemidir.
SSR Nasıl Çalışır?
Section titled “SSR Nasıl Çalışır?”1. Kullanıcı URL'e gider2. Sunucu route'un loader'ını çalıştırır3. Sunucu HTML'i oluşturur (verilerle birlikte)4. HTML tarayıcıya gönderilir5. Tarayıcı HTML'i gösterir (JS yüklenmeden)6. JS yüklendikten sonra sayfa interaktif olurVarsayılan SSR Davranışı
Section titled “Varsayılan SSR Davranışı”TanStack Start’ta tüm route’lar varsayılan olarak SSR modundadır:
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/')({ // Varsayılan olarak SSR aktiftir! component: HomePage, loader: async () => { // Bu loader sunucuda çalışır const data = await fetch('https://api.example.com/data').then(r => r.json()) return { data } },})
function HomePage() { const { data } = Route.useLoaderData()
return ( <div> <h1>Ana Sayfa</h1> <p>{data.message}</p> </div> )}SSR Avantajları
Section titled “SSR Avantajları”// SEO için mükemmel// src/routes/blog/$slug.tsxexport const Route = createFileRoute('/blog/$slug')({ loader: async ({ params }) => { // Arama motorları içeriği görebilir const post = await getBlogPost(params.slug) return post },})
// İlk yükleme hızlı (içerik hazır gelir)// Sosyal medya paylaşımları için preview görüntüleriSSR Dezavantajları
Section titled “SSR Dezavantajları”// Dezavantajlar:// - Sunucu yükü artar (her istekte HTML oluşturulur)// - TTFB (Time to First Byte) artabilir// - Karmaşık server-side logic gerektirebilir🎯 Selective SSR (Route Bazlı Kontrol)
Section titled “🎯 Selective SSR (Route Bazlı Kontrol)”TanStack Start’ın en güçlü özelliği, her route için farklı rendering modu seçebilmenizdir.
Route Seviyesinde SSR Kontrolü
Section titled “Route Seviyesinde SSR Kontrolü”import { createFileRoute } from '@tanstack/react-router'
// ✅ SSR aktif (varsayılan)export const Route = createFileRoute('/blog/$slug')({ component: BlogPost,})
// ❌ SSR kapalı (SPA mode)export const Route = createFileRoute('/admin/dashboard')({ component: AdminDashboard, // SSR'ı devre dışı bırak ssr: false,})Pratik Örnek: Hibrit Uygulama
Section titled “Pratik Örnek: Hibrit Uygulama”// src/routes/index.tsx - Ana sayfa (SSR aktif)export const Route = createFileRoute('/')({ component: HomePage, // ssr: true (varsayılan)})
// src/routes/blog/index.tsx - Blog listesi (SSR aktif)export const Route = createFileRoute('/blog')({ component: BlogList, loader: async () => { // SEO için önemli return { posts: await getAllPosts() } },})
// src/routes/admin/index.tsx - Admin panel (SSR kapalı)export const Route = createFileRoute('/admin')({ component: AdminPanel, ssr: false, // SPA mode loader: async () => { // Sadece client'te çalışır return { stats: await getAdminStats() } },})Nested Routes’da SSR Kontrolü
Section titled “Nested Routes’da SSR Kontrolü”// src/routes/dashboard.tsx - Layout (SSR kapalı)export const Route = createFileRoute('/dashboard')({ component: DashboardLayout, ssr: false, // Tüm dashboard SPA mode'da})
// src/routes/dashboard/index.tsx - Ana sayfa (Layout'tan miras alır)export const Route = createFileRoute('/dashboard/')({ component: DashboardHome, // SSR kapalı (parent'tan miras)})
// src/routes/dashboard/settings.tsx - Ayarlar (Layout'tan miras alır)export const Route = createFileRoute('/dashboard/settings')({ component: Settings, // SSR kapalı (parent'tan miras)})🔄 SPA Mode (Single Page Application)
Section titled “🔄 SPA Mode (Single Page Application)”SPA mode’da sayfa tamamen client-side render edilir. HTML ilk başta boş gelir, JS yüklendikten sonra içerik oluşturulur.
SPA Mode Ne Zaman Kullanılmalı?
Section titled “SPA Mode Ne Zaman Kullanılmalı?”✅ Kullanım Durumları:
- Admin panel ve dashboard’lar
- Giriş yapmış kullanıcı içi sayfalar
- SEO gerektirmeyen uygulamalar
- Gerçek zamanlı veri gösteren sayfalar
❌ Kullanılmamalı:
- Herkese açık içerik sayfaları
- Blog ve haber siteleri
- E-commerce ürün sayfaları
SPA Mode Örneği
Section titled “SPA Mode Örneği”import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/admin/dashboard')({ component: AdminDashboard, ssr: false, // SPA mode aktif loader: async () => { // Bu loader SADECE client'te çalışır const stats = await fetch('/api/admin/stats').then(r => r.json()) return { stats } },})
function AdminDashboard() { const { stats } = Route.useLoaderData()
return ( <div> <h1>Admin Dashboard</h1> <p>Toplam Kullanıcı: {stats.totalUsers}</p> <p>Bugünkü Görüntüleme: {stats.views}</p> </div> )}SPA Mode’un Avantajları
Section titled “SPA Mode’un Avantajları”// Avantajlar:// 1. Sunucu yükü azalır (HTML oluşturma yok)// 2. TTFH (Time to First Byte) düşük// 3. Karmaşık client-side logic daha kolay// 4. Gerçek zamanlı güncellemeler için uygun
export const Route = createFileRoute('/realtime/chat')({ component: ChatRoom, ssr: false, // WebSocket bağlantısı için ideal})🐳 Static Prerendering
Section titled “🐳 Static Prerendering”Static prerendering, sayfaların build zamanında HTML olarak oluşturulmasıdır. Her istekte değil, build sırasında bir kez oluşturulur.
Static Prerendering Nedir?
Section titled “Static Prerendering Nedir?”Build zamanı:npm run build ↓1. Tüm prerender route'ları toplanır2. Her route için loader çalıştırılır3. HTML dosyaları oluşturulur4. Dosyalar disk'e kaydedilir
Çalışma zamanı:Kullanıcı /hakkimizda'e gider ↓Statik HTML dosyası direkt sunulurPrerender Örneği
Section titled “Prerender Örneği”import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/hakkimizda')({ component: AboutPage, // Bu sayfa build zamanında prerender edilir loader: async () => { // Build zamanında bir kez çalışır return { title: 'Hakkımızda', content: 'Biz harika bir şirketiz...', } },})
function AboutPage() { const { title, content } = Route.useLoaderData()
return ( <div> <h1>{title}</h1> <p>{content}</p> </div> )}Prerender vs SSR
Section titled “Prerender vs SSR”// ✅ Prerender (Build zamanında)export const Route = createFileRoute('/about')({ component: AboutPage, loader: async () => { // npm run build sırasında bir kez çalışır return { content: 'Statik içerik' } }, // Prerender varsayılan olarak statik sayfalar için kullanılır})
// ✅ SSR (Her istekte)export const Route = createFileRoute('/blog/$slug')({ component: BlogPost, loader: async ({ params }) => { // Her istekte çalışır return await getBlogPost(params.slug) }, // Dinamik içerik için ideal})⚡ Streaming SSR
Section titled “⚡ Streaming SSR”Streaming SSR, HTML’in parça parça gönderilmesi tekniğidir. Bu sayede sayfa daha hızlı görünmeye başlar.
Streaming SSR Nasıl Çalışır?
Section titled “Streaming SSR Nasıl Çalışır?”Normal SSR:[Layout hazırlanır] → [Veri çekilir] → [İçerik hazırlanır] → [Tüm HTML gönderilir]└───────────────────── 5 saniye ─────────────────────┘
Streaming SSR:[Layout gönderilir] → [Yükleniyor gösterilir] → [Veri çekilir] → [İçerik gönderilir]└─ 100ms ─┘ └───── 2 saniye ──────┘
Kullanıcı deneyimi: 100ms'te bir şey görür, 2 saniyede içerik gelirStreaming SSR Örneği
Section titled “Streaming SSR Örneği”// src/routes/urunler/$id.tsximport { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/urunler/$id')({ component: UrunDetayPage, loader: async ({ params }) => { // Layout hemen gönderilir return { // Ana veri urun: await getUrun(params.id), // Yorumlar daha sonra gelir (streaming) yorumlar: await getYorumlar(params.id), } },})
function UrunDetayPage() { const { urun, yorumlar } = Route.useLoaderData()
return ( <div> {/* Hemen görünür */} <h1>{urun.baslik}</h1> <p>{urun.aciklama}</p>
{/* Yorumlar yüklendiğinde görünür */} <YorumlarListesi yorumlar={yorumlar} /> </div> )}Suspense ile Streaming
Section titled “Suspense ile Streaming”import { Suspense } from 'react'import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/dashboard')({ component: DashboardPage, loader: async () => { return { // Hızlı yüklenen veri user: await getCurrentUser(), } },})
function DashboardPage() { const { user } = Route.useLoaderData()
return ( <div> <h1>Hoş geldin, {user.ad}!</h1>
{/* Yavaş yüklenen bileşenler için Suspense */} <Suspense fallback={<div>Yükleniyor...</div>}> <YavasBilesen /> </Suspense> </div> )}
// Bu bileşen streaming ile yüklenirfunction YavasBilesen() { const { data } = useQuery({ queryKey: ['yavas-veri'], queryFn: () => fetch('/api/yavas').then(r => r.json()), })
return <div>{data.content}</div>}🎯 Hangi Rendering Modu Ne Zaman?
Section titled “🎯 Hangi Rendering Modu Ne Zaman?”Karar Ağacı
Section titled “Karar Ağacı”Route'u belirle ↓SEO gerekiyor mu? ├─ Evet → SSR veya Static │ ↓ │ İçerik dinamik mi? │ ├─ Evet → SSR │ └─ Hayır → Static Prerender │ └─ Hayır → SPA Mode ↓ Giriş gerekli mi? ├─ Evet → SPA Mode (mükemmel) └─ Hayır → Yine de SPA (admin için)Örnek Senaryolar
Section titled “Örnek Senaryolar”// ✅ 1. Blog Detay - SSR (SEO + Dinamik)export const Route = createFileRoute('/blog/$slug')({ component: BlogPost, // ssr: true (varsayılan) loader: async ({ params }) => await getPost(params.slug),})
// ✅ 2. Hakkımızda - Static (SEO + Statik)export const Route = createFileRoute('/hakkimizda')({ component: AboutPage, // Prerender edilir})
// ✅ 3. Admin Dashboard - SPA (SEO yok + Dinamik)export const Route = createFileRoute('/admin/dashboard')({ component: AdminDashboard, ssr: false,})
// ✅ 4. Kullanıcı Profil - SPA (SEO yok + Giriş gerekli)export const Route = createFileRoute('/profil/$userId')({ component: UserProfile, ssr: false,})
// ✅ 5. Ana Sayfa - SSR (SEO + Dinamik)export const Route = createFileRoute('/')({ component: HomePage, // ssr: true (varsayılan) loader: async () => await getFeaturedPosts(),})🎨 Pratik Örnek: E-Commerce Sitesi
Section titled “🎨 Pratik Örnek: E-Commerce Sitesi”Hadi bir e-commerce sitesi için farklı rendering modlarını kullanalım!
Site Yapısı
Section titled “Site Yapısı”// src/routes/index.tsx - Ana sayfa (SSR)export const Route = createFileRoute('/')({ component: HomePage, // SEO için SSR loader: async () => { const [featured, categories, banners] = await Promise.all([ getFeaturedProducts(), getCategories(), getBanners(), ]) return { featured, categories, banners } },})
function HomePage() { const { featured, categories, banners } = Route.useLoaderData()
return ( <div> <BannerSlider banners={banners} /> <CategoryList categories={categories} /> <ProductGrid products={featured} /> </div> )}// src/routes/urunler/$slug.tsx - Ürün detay (SSR)export const Route = createFileRoute('/urunler/$slug')({ component: UrunDetayPage, // SEO için kritik! loader: async ({ params }) => { const urun = await getUrun(params.slug)
if (!urun) { throw notFound() }
return { urun } },})
function UrunDetayPage() { const { urun } = Route.useLoaderData()
return ( <div> <h1>{urun.baslik}</h1> <p>{urun.aciklama}</p> <p>Fiyat: ₺{urun.fiyat}</p> </div> )}// src/routes/sepet.tsx - Sepet (SPA)export const Route = createFileRoute('/sepet')({ component: SepetPage, ssr: false, // SEO gerekmez loader: async () => { // Kullanıcıya özel veri return { sepet: await getSepet() } },})
function SepetPage() { const { sepet } = Route.useLoaderData()
return ( <div> <h1>Alışveriş Sepetim</h1> <SepetListesi urunler={sepet.urunler} /> </div> )}// src/routes/hesap/index.tsx - Hesap (SPA + Auth)export const Route = createFileRoute('/hesap')({ component: HesapPage, ssr: false, // Giriş gerekli loader: async () => { const session = await getSession()
if (!session.data.userId) { throw redirect({ to: '/giris' }) }
return { siparisler: await getSiparisler(session.data.userId), adresler: await getAdresler(session.data.userId), } },})
function HesapPage() { const { siparisler, adresler } = Route.useLoaderData()
return ( <div> <h1>Hesabım</h1> <SiparisListesi siparisler={siparisler} /> <AdresListesi adresler={adresler} /> </div> )}// src/routes/ss/index.tsx - SSS (Static)export const Route = createFileRoute('/sss')({ component: SSSPage, // Statik içerik, prerender edilebilir loader: async () => { return { sorular: [ { q: 'Kargo ne kadar?', a: '100 TL üzeri ücretsiz' }, { q: 'İade nasıl yapılır?', a: '30 gün içinde iade' }, ], } },})
function SSSPage() { const { sorular } = Route.useLoaderData()
return ( <div> <h1>Sıkça Sorulan Sorular</h1> {sorular.map((s) => ( <div key={s.q}> <h3>{s.q}</h3> <p>{s.a}</p> </div> ))} </div> )}✅ Ders 7 Özeti
Section titled “✅ Ders 7 Özeti”Bu derste öğrendiklerimiz:
| Konu | Açıklama |
|---|---|
| SSR | Sunucuda HTML oluşturma (varsayılan) |
| SPA Mode | Tamamen client-side rendering (ssr: false) |
| Static Prerendering | Build zamanında HTML oluşturma |
| Selective SSR | Route bazlı rendering modu seçimi |
| Streaming SSR | Parça parça HTML gönderme |
| Suspense | Yavaş yüklenen bileşenler için loading |
Rendering Modu Karar Tablosu
Section titled “Rendering Modu Karar Tablosu”| Senaryo | Önerilen Mod |
|---|---|
| Blog yazısı | SSR |
| Ana sayfa | SSR |
| Admin panel | SPA |
| Kullanıcı profili | SPA |
| Hakkımızda sayfası | Static |
| SSS sayfası | Static |
| Sepet | SPA |
| Ürün detay | SSR |
📝 Alıştırmalar
Section titled “📝 Alıştırmalar”Alıştırma 1: Blog Uygulaması
Section titled “Alıştırma 1: Blog Uygulaması”Blog uygulamanız için her sayfaya uygun rendering modunu seçin:
// Blog listesi -> ?// Blog detay -> ?// Yorum yaz (giriş gerekli) -> ?// Yazar profili -> ?Alıştırma 2: Admin Paneli
Section titled “Alıştırma 2: Admin Paneli”Admin panelinizi SPA mode’a çevirin:
// Tüm admin route'ları için ssr: false yapın// Nested routes için inheritance'ı test edinAlıştırma 3: Hybrid Yapı
Section titled “Alıştırma 3: Hybrid Yapı”Şu yapının her route’u için doğru modu seçin:
/ (ana sayfa) ├── /blog (blog listesi) │ └── /blog/$slug (detay) ├── /urunler (ürün listesi) │ └── /urunler/$id (ürün detay) ├── /sepet (sepet) └── /admin (admin panel) ├── /admin/dashboard └── /admin/urunler🚀 Sonraki Ders: Authentication ve Authorization
Section titled “🚀 Sonraki Ders: Authentication ve Authorization”Bir sonraki derste şunları öğreneceksiniz:
- 🔐 Authentication vs Authorization farkı
- 🍪 Session tabanlı auth
- 🔑 JWT token yönetimi
- 🛡️ Route koruma (protected routes)
- 📋 Role-based access control
- 🎯 OAuth entegrasyonu
💬 Sorularınız?
Section titled “💬 Sorularınız?”- SSR her zaman daha mı iyidir?
- SPA mode SEO için hiç uygun değil mi?
- Static prerendering ne zaman yeniden yapılır?
Bir sonraki derste görüşmek üzere! 👋