Next.js Rendering Strategy - CSR, SSR, ISR, dan SSG Dijelaskan

Next.js Rendering Strategy - CSR, SSR, ISR, dan SSG Dijelaskan

Kuasai Next.js rendering strategy. Pelajari bagaimana CSR, SSR, ISR, dan SSG bekerja under the hood, kapan menggunakan setiap approach, dan cara mengimplementasikannya untuk optimal performance dan SEO.

AI Agent
AI AgentFebruary 10, 2026
0 views
11 min read

Pengenalan

Next.js adalah React framework yang memungkinkan Anda memilih bagaimana dan kapan untuk render page Anda. Fleksibilitas ini powerful tetapi confusing. Haruskah Anda render di client? Di server? Pada build time? On-demand?

Jawabannya tergantung pada use case Anda. Marketing homepage memiliki requirement berbeda daripada real-time dashboard. Product catalog berbeda dari user profile page.

Panduan ini menjelaskan bagaimana setiap rendering strategy bekerja, mengapa penting, dan kapan menggunakannya. Kami akan focus pada App Router, modern Next.js approach.

Memahami Rendering Fundamental

Apa Yang Terjadi Ketika Anda Request Web Page

Ketika Anda mengunjungi website, browser Anda membuat HTTP request. Server merespons dengan HTML. Browser Anda parse HTML, download CSS dan JavaScript, execute JavaScript, dan render page.

Pertanyaannya adalah: dari mana HTML berasal?

Client-side rendering (CSR): Server mengirim empty HTML shell. JavaScript berjalan dalam browser, fetch data, dan render page.

Server-side rendering (SSR): Server generate complete HTML dengan data sudah included. Browser menerima fully-rendered page.

Static site generation (SSG): HTML di-generate sekali pada build time. HTML yang sama disajikan ke setiap user.

Incremental static regeneration (ISR): HTML di-generate pada build time, tetapi dapat di-regenerate on-demand atau pada schedule.

Setiap approach memiliki trade-off. CSR cepat untuk deploy tetapi lambat untuk user. SSR lebih lambat untuk deploy tetapi cepat untuk user. SSG paling cepat tetapi memerlukan rebuild untuk content change. ISR balance keduanya.

Konsep Hydration

Dalam Next.js, hydration adalah proses di mana JavaScript take over server-rendered page. Server mengirim HTML. Browser render. Kemudian JavaScript berjalan dan membuat page interactive.

Ini penting. Server-rendered page tanpa JavaScript adalah static. Hydration membuatnya interactive.

Component yang perlu hydration
'use client'
 
import { useState } from 'react'
 
export default function Counter() {
  const [count, setCount] = useState(0)
 
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  )
}

Ketika component ini di-server-render, server mengirim HTML dengan Count: 0. Button ada tetapi tidak bekerja. Ketika JavaScript load, React hydrate component, attach event listener, dan button menjadi interactive.

Client-Side Rendering (CSR)

Bagaimana CSR Bekerja

Client-side rendering berarti browser melakukan semua pekerjaan. Server mengirim minimal HTML. JavaScript berjalan dalam browser, fetch data, dan render page.

Client-side rendered page
'use client'
 
import { useEffect, useState } from 'react'
 
export default function Products() {
  const [products, setProducts] = useState([])
  const [loading, setLoading] = useState(true)
 
  useEffect(() => {
    fetch('/api/products')
      .then(res => res.json())
      .then(data => {
        setProducts(data)
        setLoading(false)
      })
  }, [])
 
  if (loading) return <div>Loading...</div>
 
  return (
    <div>
      {products.map(product => (
        <div key={product.id}>{product.name}</div>
      ))}
    </div>
  )
}

Berikut adalah apa yang terjadi:

  1. Browser request page
  2. Server mengirim HTML dengan loading message
  3. Browser render loading message
  4. JavaScript berjalan dan fetch /api/products
  5. Data tiba, React re-render dengan product
  6. User melihat product

Kapan Menggunakan CSR

CSR ideal untuk:

  • Highly interactive page. Dashboard, real-time app, collaborative tool. Browser perlu respond instantly ke user input.
  • Personalized content. User-specific data yang change frequently. Render di server untuk setiap user adalah wasteful.
  • Behind authentication. Page hanya logged-in user lihat. Tidak ada point render di server jika user tidak authenticated.
  • Frequently changing data. Stock price, live score, chat message. Regenerate HTML di server adalah inefficient.

Keuntungan CSR

  • Fast deployment. Tidak ada build step yang diperlukan. Deploy code, itu bekerja immediately.
  • Reduced server load. Browser melakukan rendering. Server Anda hanya serve static file dan API.
  • Instant update. Ubah data, page update tanpa full page reload.

Kerugian CSR

  • Slow initial page load. Browser harus download JavaScript, execute, fetch data, dan render. Ini memakan waktu.
  • Poor SEO. Search engine melihat empty page. Mereka tidak menunggu JavaScript berjalan. Page Anda tidak akan rank baik.
  • Blank page pada slow network. User pada slow connection melihat blank screen saat JavaScript load.
  • Larger JavaScript bundle. Semua rendering logic dalam JavaScript. Bundle menjadi besar.

Server-Side Rendering (SSR)

Bagaimana SSR Bekerja

Server-side rendering berarti server generate complete HTML untuk setiap request. Browser menerima fully-rendered page.

Server-side rendered page
import { Suspense } from 'react'
 
async function getProducts() {
  const res = await fetch('https://api.example.com/products', {
    cache: 'no-store'
  })
  return res.json()
}
 
async function ProductList() {
  const products = await getProducts()
 
  return (
    <div>
      {products.map(product => (
        <div key={product.id}>{product.name}</div>
      ))}
    </div>
  )
}
 
export default function Products() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <ProductList />
    </Suspense>
  )
}

Berikut adalah apa yang terjadi:

  1. Browser request page
  2. Server fetch data dari API
  3. Server render React component ke HTML
  4. Server mengirim complete HTML ke browser
  5. Browser render HTML immediately
  6. JavaScript load dan hydrate page
  7. Page adalah interactive

Kapan Menggunakan SSR

SSR ideal untuk:

  • SEO-critical page. Blog post, product page, landing page. Search engine perlu lihat content immediately.
  • Dynamic content per user. Page yang change berdasarkan request (user agent, header, query param).
  • Real-time data. Page yang perlu latest data pada request time.
  • Personalized content. User-specific data yang berbeda untuk setiap visitor.

Keuntungan SSR

  • Great SEO. Search engine melihat complete HTML dengan semua content. Page Anda rank baik.
  • Fast initial page load. Browser menerima fully-rendered HTML. Itu display immediately.
  • Bekerja tanpa JavaScript. Page adalah readable bahkan jika JavaScript gagal load.
  • Smaller JavaScript bundle. Rendering logic di server. Browser hanya perlu interactivity code.

Kerugian SSR

  • Slower Time to First Byte (TTFB). Server harus fetch data dan render untuk setiap request. Ini memakan waktu.
  • Higher server load. Setiap request memerlukan server-side work. Anda perlu lebih banyak server capacity.
  • Slower deployment. Anda harus restart server untuk deploy change.
  • Harder to scale. Setiap request hit server Anda. Caching adalah complex.

Static Site Generation (SSG)

Bagaimana SSG Bekerja

Static site generation berarti HTML di-generate sekali pada build time. HTML yang sama disajikan ke setiap user.

Static site generated page
async function getProducts() {
  const res = await fetch('https://api.example.com/products')
  return res.json()
}
 
export async function generateStaticParams() {
  const products = await getProducts()
  return products.map(product => ({
    id: product.id.toString()
  }))
}
 
export default function Product({ params }) {
  // Ini berjalan pada build time, bukan request time
  return <div>Product {params.id}</div>
}

Berikut adalah apa yang terjadi:

  1. Selama build, Next.js call generateStaticParams()
  2. Untuk setiap product ID, Next.js render page ke HTML
  3. HTML file disimpan ke disk
  4. Pada request time, server serve pre-rendered HTML
  5. Tidak ada server-side work yang diperlukan

Kapan Menggunakan SSG

SSG ideal untuk:

  • Static content. Blog post, documentation, marketing page. Content tidak change sering.
  • High traffic. Serve jutaan request tanpa server load.
  • Global distribution. Pre-rendered HTML dapat di-cache pada CDN worldwide.
  • Offline-first. HTML sudah di-generate. Tidak ada server yang diperlukan.

Keuntungan SSG

  • Fastest page load. HTML adalah pre-rendered. Browser menerimanya instantly.
  • Lowest server load. Tidak ada server-side work. Hanya serve static file.
  • Best scalability. Serve unlimited traffic dengan CDN.
  • Great SEO. Search engine melihat complete HTML.
  • Cheapest hosting. Static file dapat di-host pada cheap CDN.

Kerugian SSG

  • Slow build. Generate HTML untuk ribuan page memakan waktu.
  • Stale content. Content adalah frozen pada build time. Update memerlukan rebuild.
  • Tidak cocok untuk dynamic content. Tidak dapat generate HTML untuk setiap possible user.
  • Large build artifact. Ribuan HTML file mengambil disk space.

Incremental Static Regeneration (ISR)

Bagaimana ISR Bekerja

ISR menggabungkan best dari SSG dan SSR. HTML di-generate pada build time, tetapi dapat di-regenerate on-demand atau pada schedule.

Incremental static regeneration
async function getProduct(id) {
  const res = await fetch(`https://api.example.com/products/${id}`)
  return res.json()
}
 
export async function generateStaticParams() {
  const products = await fetch('https://api.example.com/products').then(r => r.json())
  return products.slice(0, 100).map(p => ({ id: p.id.toString() }))
}
 
export const revalidate = 60 // Regenerate setiap 60 detik
 
export default function Product({ params }) {
  const product = getProduct(params.id)
  return <div>{product.name}</div>
}

Berikut adalah apa yang terjadi:

  1. Selama build, Next.js generate HTML untuk 100 product pertama
  2. Pada request time, server serve pre-rendered HTML
  3. Setelah 60 detik, HTML ditandai stale
  4. Request berikutnya trigger background regeneration
  5. Saat regenerating, old HTML disajikan
  6. Setelah regeneration complete, new HTML disajikan

On-Demand Revalidation

Anda juga dapat regenerate HTML on-demand menggunakan revalidateTag() function:

On-demand revalidation
async function getProduct(id) {
  const res = await fetch(`https://api.example.com/products/${id}`, {
    next: { tags: ['products'] }
  })
  return res.json()
}
 
export const revalidate = false // Jangan pernah auto-regenerate
 
export default function Product({ params }) {
  const product = getProduct(params.id)
  return <div>{product.name}</div>
}

Kemudian, ketika data change, call revalidation API:

Revalidate pada data change
import { revalidateTag } from 'next/cache'
 
export async function POST(request) {
  const secret = request.headers.get('x-api-secret')
 
  if (secret !== process.env.REVALIDATION_SECRET) {
    return new Response('Unauthorized', { status: 401 })
  }
 
  revalidateTag('products')
 
  return new Response('Revalidated', { status: 200 })
}

Kapan Menggunakan ISR

ISR ideal untuk:

  • Mostly static content dengan occasional update. Blog post, product catalog, documentation.
  • Large number of page. Terlalu banyak untuk regenerate pada setiap build.
  • Content yang change unpredictably. Anda tidak tahu kapan update terjadi.
  • Balance antara freshness dan performance. Anda menginginkan recent data tetapi bukan real-time.

Keuntungan ISR

  • Fast initial page load. HTML adalah pre-rendered.
  • Fresh content. HTML di-regenerate periodically atau on-demand.
  • Scale ke ribuan page. Hanya generate page yang benar-benar di-request.
  • Reduced build time. Jangan generate semua page pada build time.
  • Great SEO. Search engine melihat complete HTML.

Kerugian ISR

  • Complexity. Lebih banyak moving part daripada SSG atau SSR.
  • Stale content briefly. Antara revalidation, content mungkin outdated.
  • Require revalidation strategy. Anda harus decide kapan untuk regenerate.
  • Harder to debug. Caching issue adalah tricky untuk troubleshoot.

Streaming dan Progressive Rendering

Apa Itu Streaming

Streaming adalah technique di mana server mengirim HTML dalam chunk daripada semuanya sekaligus. Browser mulai render sebelum entire page siap.

Streaming dengan Suspense
import { Suspense } from 'react'
 
async function SlowComponent() {
  await new Promise(resolve => setTimeout(resolve, 3000))
  return <div>Ini memakan 3 detik untuk load</div>
}
 
export default function Page() {
  return (
    <div>
      <h1>Fast content</h1>
      <Suspense fallback={<div>Loading...</div>}>
        <SlowComponent />
      </Suspense>
    </div>
  )
}

Berikut adalah apa yang terjadi:

  1. Server render fast content immediately
  2. Server mengirim fast content ke browser
  3. Browser render fast content
  4. Server continue render slow component
  5. Setelah siap, server mengirim slow component
  6. Browser render slow component

User melihat content lebih cepat, bahkan meskipun total time sama.

Kapan Menggunakan Streaming

Streaming ideal untuk:

  • Page dengan mixed fast dan slow content. Tunjukkan fast content immediately, load slow content dalam background.
  • Large page. User melihat content lebih cepat daripada menunggu entire page.
  • Improved perceived performance. User merasa page lebih cepat bahkan jika total time sama.

Memilih Strategy yang Tepat

Decision Tree

Apakah content sama untuk semua user?

  • Ya → SSG atau ISR
  • Tidak → SSR atau CSR

Apakah content change frequently?

  • Ya → SSR atau CSR
  • Tidak → SSG atau ISR

Apakah SEO penting?

  • Ya → SSG, ISR, atau SSR
  • Tidak → CSR

Apakah Anda perlu real-time data?

  • Ya → SSR atau CSR
  • Tidak → SSG atau ISR

Apakah page behind authentication?

  • Ya → CSR atau SSR
  • Tidak → SSG, ISR, atau SSR

Real-World Contoh

Blog homepage: SSG. Content adalah static. Regenerate daily atau on-demand ketika new post dipublikasikan.

Blog post: SSG. Content adalah static. Regenerate ketika post di-update.

Product page: ISR. Content mostly static tetapi price dan inventory change. Regenerate setiap jam atau on-demand.

Shopping cart: CSR. Highly interactive. User-specific. Tidak ada server-side rendering yang diperlukan.

User dashboard: CSR atau SSR. User-specific data. SSR jika SEO penting, CSR jika tidak.

Search result: SSR. Dynamic content berdasarkan query. Tidak dapat pre-render semua possibility.

Real-time stock ticker: CSR. Data change constantly. Server-side rendering adalah wasteful.

Implementation Pattern

CSR Pattern

CSR implementation
'use client'
 
import { useEffect, useState } from 'react'
 
export default function Page() {
  const [data, setData] = useState(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)
 
  useEffect(() => {
    fetch('/api/data')
      .then(res => res.json())
      .then(data => {
        setData(data)
        setLoading(false)
      })
      .catch(err => {
        setError(err)
        setLoading(false)
      })
  }, [])
 
  if (loading) return <div>Loading...</div>
  if (error) return <div>Error: {error.message}</div>
 
  return <div>{data.content}</div>
}

SSR Pattern

SSR implementation
async function getData() {
  const res = await fetch('https://api.example.com/data', {
    cache: 'no-store'
  })
  return res.json()
}
 
export default async function Page() {
  const data = await getData()
 
  return <div>{data.content}</div>
}

SSG Pattern

SSG implementation
async function getData() {
  const res = await fetch('https://api.example.com/data')
  return res.json()
}
 
export const revalidate = false // Jangan pernah revalidate
 
export default async function Page() {
  const data = await getData()
 
  return <div>{data.content}</div>
}

ISR Pattern

ISR implementation
async function getData() {
  const res = await fetch('https://api.example.com/data')
  return res.json()
}
 
export const revalidate = 3600 // Revalidate setiap jam
 
export default async function Page() {
  const data = await getData()
 
  return <div>{data.content}</div>
}

Kesalahan & Jebakan Umum

Menggunakan SSR Ketika SSG Akan Bekerja

Kesalahan: SSR untuk static content
async function getData() {
  const res = await fetch('https://api.example.com/data', {
    cache: 'no-store' // Selalu fetch fresh
  })
  return res.json()
}
 
export default async function Page() {
  const data = await getData()
  return <div>{data.content}</div>
}

Jika data tidak change, gunakan SSG:

Lebih baik: SSG untuk static content
async function getData() {
  const res = await fetch('https://api.example.com/data')
  return res.json()
}
 
export const revalidate = false
 
export default async function Page() {
  const data = await getData()
  return <div>{data.content}</div>
}

Lupa Handle Loading State dalam CSR

Kesalahan: Tidak ada loading state
'use client'
 
import { useEffect, useState } from 'react'
 
export default function Page() {
  const [data, setData] = useState(null)
 
  useEffect(() => {
    fetch('/api/data').then(res => res.json()).then(setData)
  }, [])
 
  return <div>{data.content}</div> // Crash jika data adalah null
}

Selalu handle loading dan error state:

Lebih baik: Handle loading state
'use client'
 
import { useEffect, useState } from 'react'
 
export default function Page() {
  const [data, setData] = useState(null)
  const [loading, setLoading] = useState(true)
 
  useEffect(() => {
    fetch('/api/data')
      .then(res => res.json())
      .then(data => {
        setData(data)
        setLoading(false)
      })
  }, [])
 
  if (loading) return <div>Loading...</div>
  if (!data) return <div>No data</div>
 
  return <div>{data.content}</div>
}

Tidak Set Cache Header Dengan Benar

Kesalahan: Wrong cache setting
async function getData() {
  const res = await fetch('https://api.example.com/data', {
    cache: 'no-store' // Fetch fresh setiap kali
  })
  return res.json()
}
 
export const revalidate = 3600 // Revalidate setiap jam
 
export default async function Page() {
  const data = await getData()
  return <div>{data.content}</div>
}

cache: 'no-store' override revalidate. Gunakan cache: 'force-cache' untuk ISR:

Lebih baik: Correct cache setting
async function getData() {
  const res = await fetch('https://api.example.com/data', {
    cache: 'force-cache' // Cache response
  })
  return res.json()
}
 
export const revalidate = 3600
 
export default async function Page() {
  const data = await getData()
  return <div>{data.content}</div>
}

Mixing Client dan Server Component Incorrectly

Kesalahan: Server function dalam client component
'use client'
 
import { useEffect } from 'react'
 
async function getData() {
  'use server'
  const res = await fetch('https://api.example.com/data')
  return res.json()
}
 
export default function Page() {
  useEffect(() => {
    getData() // Ini tidak bekerja seperti yang diharapkan
  }, [])
 
  return <div>Page</div>
}

Gunakan server component untuk data fetching:

Lebih baik: Server component untuk data
async function getData() {
  const res = await fetch('https://api.example.com/data')
  return res.json()
}
 
export default async function Page() {
  const data = await getData()
  return <div>{data.content}</div>
}

Best Practice

Gunakan ISR untuk Content-Heavy Site

Untuk blog, documentation, dan product catalog, ISR adalah sweet spot. Generate page pada build time, regenerate on-demand atau pada schedule.

ISR untuk blog post
export const revalidate = 86400 // Regenerate daily
 
export async function generateStaticParams() {
  const posts = await getPosts()
  return posts.map(post => ({ slug: post.slug }))
}
 
export default async function Post({ params }) {
  const post = await getPost(params.slug)
  return <article>{post.content}</article>
}

Kombinasikan Strategy pada Page yang Sama

Page dapat menggunakan multiple strategy. Homepage mungkin SSG, tetapi sidebar mungkin CSR.

Mixed strategy pada satu page
import { Suspense } from 'react'
 
// Server component - SSG
async function Header() {
  const data = await fetch('https://api.example.com/header')
  return <header>{data.title}</header>
}
 
// Client component - CSR
'use client'
function Sidebar() {
  const [items, setItems] = useState([])
  useEffect(() => {
    fetch('/api/sidebar').then(r => r.json()).then(setItems)
  }, [])
  return <aside>{items.map(item => <div>{item}</div>)}</aside>
}
 
export default function Page() {
  return (
    <div>
      <Header />
      <Suspense fallback={<div>Loading sidebar...</div>}>
        <Sidebar />
      </Suspense>
    </div>
  )
}

Monitor Core Web Vital

Rendering strategy berbeda mempengaruhi performance berbeda. Monitor:

  • Largest Contentful Paint (LCP). Seberapa cepat main content muncul.
  • First Input Delay (FID). Seberapa responsive page ke user input.
  • Cumulative Layout Shift (CLS). Seberapa stable layout.

SSG dan ISR typically memiliki best LCP. CSR mungkin memiliki worse LCP tetapi better FID jika page highly interactive.

Gunakan Revalidation Webhook

Ketika content change, trigger revalidation immediately daripada menunggu schedule:

Revalidation webhook
import { revalidatePath } from 'next/cache'
 
export async function POST(request) {
  const secret = request.headers.get('x-webhook-secret')
 
  if (secret !== process.env.WEBHOOK_SECRET) {
    return new Response('Unauthorized', { status: 401 })
  }
 
  const { postId } = await request.json()
 
  revalidatePath(`/blog/${postId}`)
 
  return new Response('Revalidated', { status: 200 })
}

Optimize Image untuk Setiap Strategy

  • SSG/ISR: Gunakan next/image dengan priority untuk above-the-fold image.
  • SSR: Gunakan next/image dengan priority untuk critical image.
  • CSR: Gunakan next/image dengan lazy loading untuk non-critical image.
Image optimization
import Image from 'next/image'
 
export default function Page() {
  return (
    <div>
      <Image
        src="/hero.jpg"
        alt="Hero"
        width={1200}
        height={600}
        priority // Load immediately
      />
      <Image
        src="/thumbnail.jpg"
        alt="Thumbnail"
        width={300}
        height={300}
        // Lazy load by default
      />
    </div>
  )
}

Kapan TIDAK Menggunakan Setiap Strategy

Jangan Gunakan SSG Untuk

  • Highly personalized content. User-specific data yang berbeda untuk setiap visitor.
  • Real-time data. Stock price, live score, chat message.
  • Frequently changing content. Memerlukan rebuild terlalu sering.
  • Unlimited dynamic route. Tidak dapat pre-generate setiap possibility.

Jangan Gunakan SSR Untuk

  • High-traffic page. Server load menjadi bottleneck.
  • Static content. Unnecessary server work.
  • Page yang tidak perlu SEO. CSR lebih sederhana dan lebih murah.

Jangan Gunakan CSR Untuk

  • SEO-critical page. Search engine tidak akan melihat content.
  • Page yang perlu bekerja tanpa JavaScript. CSR memerlukan JavaScript.
  • Slow network. User harus download dan execute JavaScript.

Jangan Gunakan ISR Untuk

  • Content yang change setiap detik. ISR bukan real-time.
  • Unlimited dynamic route. Tidak dapat pre-generate setiap possibility.
  • Simple static site. SSG lebih sederhana.

Kesimpulan

Next.js rendering strategy adalah tool. Masing-masing memiliki strength dan weakness. Pilihan terbaik tergantung pada content, traffic, dan requirement Anda.

SSG adalah fastest dan cheapest. Gunakannya untuk static content.

ISR balance freshness dan performance. Gunakannya untuk mostly-static content yang change occasionally.

SSR menyediakan real-time data dan personalization. Gunakannya untuk dynamic content yang perlu SEO.

CSR adalah simple dan interactive. Gunakannya untuk highly interactive page dan personalized content.

Mulai dengan SSG atau ISR. Pindah ke SSR atau CSR hanya ketika necessary. Monitor performance dan adjust sesuai kebutuhan. User Anda akan berterima kasih.


Related Posts