SSR vs SSG di Next.js: Perbedaan dan Kapan Menggunakannya
Pendahuluan
Salah satu keputusan paling krusial saat membangun aplikasi dengan Next.js adalah memilih strategi rendering yang tepat. Apakah halaman harus di-render di server setiap kali ada request? Atau cukup di-generate sekali saat build dan disajikan sebagai file statis?
Di sinilah SSR (Server-Side Rendering) dan SSG (Static Site Generation) memainkan perannya. Keduanya bisa menghasilkan HTML yang siap dibaca mesin pencari, tetapi cara kerjanya — dan kapan kamu harus memilih masing-masing — sangat berbeda.
Artikel ini akan membedah perbedaan keduanya secara mendalam, lengkap dengan contoh kode nyata dan panduan memilih strategi yang paling sesuai untuk proyekmu.
Memahami Server-Side Rendering (SSR) di Next.js
SSR berarti setiap kali pengguna membuka halaman, server akan memproses request tersebut, mengambil data terbaru, lalu mengembalikan HTML yang sudah terisi ke browser. Proses ini terjadi on-demand — setiap request, server bekerja.
Di Next.js (App Router), SSR diaktifkan dengan menggunakan async component dan melakukan fetch data di dalam component itu sendiri tanpa opsi cache:
// app/products/page.tsx
export default async function ProductsPage() {
// Fetch dilakukan setiap request (SSR)
const res = await fetch('https://api.toko.com/products', {
cache: 'no-store', // kunci utama SSR
});
const products = await res.json();
return (
<main>
<h1>Daftar Produk Terbaru</h1>
<ul>
{products.map((product: { id: number; name: string; price: number }) => (
<li key={product.id}>
{product.name} — Rp {product.price.toLocaleString('id-ID')}
</li>
))}
</ul>
</main>
);
}
Di Pages Router (cara lama), SSR diimplementasikan dengan getServerSideProps:
// pages/products.tsx
import type { GetServerSideProps } from 'next';
type Product = { id: number; name: string; price: number };
export const getServerSideProps: GetServerSideProps = async (context) => {
const res = await fetch('https://api.toko.com/products');
const products: Product[] = await res.json();
return {
props: { products },
};
};
export default function ProductsPage({ products }: { products: Product[] }) {
return (
<ul>
{products.map((p) => (
<li key={p.id}>{p.name} — Rp {p.price.toLocaleString('id-ID')}</li>
))}
</ul>
);
}
Kapan menggunakan SSR?
- Data berubah sangat sering (harga saham, stok produk real-time)
- Konten bersifat personal (dashboard pengguna, feed berdasarkan login)
- Data harus selalu fresh saat halaman dibuka
Bayangkan kamu ingin membangun fitur pelacak order seperti yang ada di marketplace besar — statusnya bisa berubah kapan saja, jadi SSR adalah pilihan yang tepat.
Memahami Static Site Generation (SSG) di Next.js
SSG berarti HTML di-generate satu kali saat proses npm run build. Hasilnya disimpan sebagai file statis yang bisa langsung disajikan oleh CDN tanpa komputasi tambahan di server.
Di App Router, komponen yang melakukan fetch tanpa cache: 'no-store' secara default akan di-cache (SSG/static):
// app/blog/[slug]/page.tsx
type Params = { slug: string };
type Post = { title: string; content: string; slug: string };
// Tentukan halaman mana saja yang akan di-generate saat build
export async function generateStaticParams(): Promise<Params[]> {
const res = await fetch('https://api.blog.com/posts');
const posts: Post[] = await res.json();
return posts.map((post) => ({
slug: post.slug,
}));
}
export default async function BlogPostPage({ params }: { params: Params }) {
const res = await fetch(`https://api.blog.com/posts/${params.slug}`);
const post: Post = await res.json();
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
);
}
Di Pages Router, SSG menggunakan getStaticProps dan opsional getStaticPaths:
// pages/blog/[slug].tsx
import type { GetStaticProps, GetStaticPaths } from 'next';
type Post = { title: string; content: string; slug: string };
export const getStaticPaths: GetStaticPaths = async () => {
const res = await fetch('https://api.blog.com/posts');
const posts: Post[] = await res.json();
return {
paths: posts.map((p) => ({ params: { slug: p.slug } })),
fallback: false, // 404 untuk slug yang tidak ada
};
};
export const getStaticProps: GetStaticProps = async ({ params }) => {
const res = await fetch(`https://api.blog.com/posts/${params?.slug}`);
const post: Post = await res.json();
return { props: { post } };
};
export default function BlogPost({ post }: { post: Post }) {
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
);
}
Kapan menggunakan SSG?
- Konten jarang berubah (dokumentasi, artikel blog, halaman landing)
- Performa dan kecepatan load adalah prioritas utama
- Traffic tinggi dan kamu ingin meminimalkan beban server
Jika kamu sedang membangun portal berita teknologi atau dokumentasi produk, SSG + CDN bisa melayani jutaan request tanpa server bekerja keras.
Perbandingan SSR, SSG, dan ISR
Sebelum masuk ke ISR, mari kita lihat perbandingan langsung ketiga strategi ini:
| Aspek | SSR | SSG | ISR |
|---|---|---|---|
| Waktu generate HTML | Setiap request | Saat build | Saat build + background |
| Kecepatan respons | Sedang | Sangat cepat | Sangat cepat |
| Kebaruan data | Selalu fresh | Hanya saat build | Sesuai interval revalidate |
| Beban server | Tinggi | Minimal | Minimal |
| Cocok untuk | Dashboard, real-time | Blog, dokumentasi | Katalog, artikel |
Jalan Tengah: Incremental Static Regeneration (ISR)
ISR adalah fitur eksklusif Next.js yang menggabungkan keunggulan SSG (kecepatan) dengan fleksibilitas SSR (data bisa diperbarui). Halaman di-generate secara statis, tetapi akan di-regenerate di background setelah interval waktu tertentu.
// App Router — gunakan revalidate
// app/articles/page.tsx
export const revalidate = 60; // regenerate setiap 60 detik
export default async function ArticlesPage() {
const res = await fetch('https://api.blog.com/articles');
const articles = await res.json();
return (
<ul>
{articles.map((a: { id: number; title: string }) => (
<li key={a.id}>{a.title}</li>
))}
</ul>
);
}
// Pages Router — gunakan revalidate di getStaticProps
import type { GetStaticProps } from 'next';
type Article = { id: number; title: string };
export const getStaticProps: GetStaticProps = async () => {
const res = await fetch('https://api.blog.com/articles');
const articles: Article[] = await res.json();
return {
props: { articles },
revalidate: 60, // regenerate setiap 60 detik
};
};
export default function ArticlesPage({ articles }: { articles: Article[] }) {
return (
<ul>
{articles.map((a) => (
<li key={a.id}>{a.title}</li>
))}
</ul>
);
}
ISR sangat ideal untuk halaman yang datanya berubah, tapi tidak perlu selalu real-time — misalnya halaman katalog produk, ranking, atau artikel yang diperbarui beberapa kali sehari.
Contoh Kasus Nyata
| Halaman | Strategi | Alasan |
|---|---|---|
| Halaman utama toko online | SSG + ISR | Konten sering sama, performa penting |
| Halaman detail produk | ISR (60–300 detik) | Stok/harga bisa berubah, tapi bukan real-time |
| Dashboard pengguna | SSR | Data personal, harus selalu akurat |
| Artikel blog | SSG | Jarang berubah, performa maksimal |
| Hasil pencarian | SSR | Query unik tiap user |
| Halaman status order | SSR | Status berubah kapan saja |
Jika kamu membangun layanan seperti marketplace skala menengah, kamu bisa menggabungkan ketiganya: halaman listing produk pakai ISR, halaman cart dan checkout pakai SSR, dan halaman statis seperti “Tentang Kami” pakai SSG murni.
Untuk proyek yang lebih kompleks, kamu juga bisa menggabungkan strategi rendering ini dengan teknik optimasi performa seperti yang dibahas di artikel React, karena prinsip-prinsip dasar performa component tetap berlaku di Next.js.
Selain itu, jika kamu berencana men-deploy ke Cloudflare Pages atau menggunakan CDN, penting untuk memahami cara kerja cache agar SSG dan ISR bekerja optimal — lihat panduan optimasi caching di Cloudflare untuk referensi lebih lanjut.
Troubleshooting: Error yang Sering Muncul
Halaman SSG Tidak Update Meskipun Data Sudah Berubah
Penyebab: Halaman menggunakan SSG tanpa ISR, sehingga HTML hanya di-generate saat build. Perubahan data di API tidak otomatis tercermin di halaman yang sudah di-deploy.
Solusi: Tambahkan revalidate untuk mengaktifkan ISR agar halaman diperbarui secara berkala:
// App Router — app/products/page.tsx
export const revalidate = 300; // regenerate setiap 5 menit
export default async function ProductsPage() {
const res = await fetch('https://api.toko.com/products');
const products = await res.json();
// ...
}
// Pages Router — di getStaticProps
export const getStaticProps: GetStaticProps = async () => {
const res = await fetch('https://api.toko.com/products');
const products = await res.json();
return {
props: { products },
revalidate: 300, // regenerate setiap 5 menit
};
};
Error: cookies() / headers() Tidak Bisa Digunakan di Static Page
Penyebab: Kamu mencoba mengakses cookies() atau headers() dari next/headers di dalam halaman atau component yang di-cache sebagai static. Fungsi ini hanya tersedia di dynamic (SSR) context.
Solusi: Tambahkan export const dynamic = 'force-dynamic' untuk memaksa halaman menjadi SSR:
// app/dashboard/page.tsx
import { cookies } from 'next/headers';
// Paksa halaman menjadi dynamic (SSR)
export const dynamic = 'force-dynamic';
export default async function DashboardPage() {
const cookieStore = cookies();
const token = cookieStore.get('auth-token');
if (!token) {
return <div>Silakan login terlebih dahulu.</div>;
}
return <div>Selamat datang di Dashboard!</div>;
}
getStaticPaths Mengembalikan 404 untuk Slug yang Valid
Penyebab: Slug tidak ada di array paths yang dikembalikan getStaticPaths, dan fallback di-set ke false sehingga semua path di luar daftar langsung di-404.
Solusi: Ubah fallback ke 'blocking' agar slug baru di-generate secara on-demand:
// pages/blog/[slug].tsx
export const getStaticPaths: GetStaticPaths = async () => {
const res = await fetch('https://api.blog.com/posts');
const posts = await res.json();
return {
paths: posts.map((p: { slug: string }) => ({
params: { slug: p.slug },
})),
// 'blocking' = slug baru di-generate on-demand, bukan langsung 404
fallback: 'blocking',
};
};
Pertanyaan yang Sering Diajukan
Apa perbedaan utama antara SSR dan SSG di Next.js?
SSR men-generate HTML di server setiap kali ada request, sehingga data selalu fresh tapi ada overhead komputasi per request. SSG men-generate HTML satu kali saat build dan hasilnya disajikan langsung dari CDN — sangat cepat, tapi data hanya seakurat saat terakhir build. Keduanya menghasilkan HTML yang SEO-friendly, perbedaannya ada pada kapan HTML itu dibuat.
Bagaimana cara memilih antara SSR, SSG, dan ISR?
Gunakan SSG jika kontenmu jarang berubah dan tidak bergantung pada data pengguna. Gunakan ISR jika konten berubah tapi tidak harus real-time — misalnya katalog produk atau artikel. Gunakan SSR jika data sangat dinamis atau bersifat personal, seperti dashboard atau halaman yang bergantung pada siapa yang sedang login.
Apakah bisa menggunakan SSR dan SSG di halaman berbeda dalam satu project Next.js?
Ya, Next.js memang dirancang untuk ini. Setiap halaman (page.tsx di App Router, atau file di pages/ di Pages Router) bisa memiliki strategi rendering yang berbeda secara independen. Kamu bisa punya halaman blog yang SSG, dashboard yang SSR, dan halaman produk yang ISR — semua dalam satu project yang sama.
Mengapa halaman SSG lebih cepat dari SSR?
Karena halaman SSG sudah berupa file HTML statis yang disimpan di CDN. Saat pengguna membuka halaman, CDN langsung mengirim file tersebut tanpa perlu menunggu server menjalankan kode atau mengambil data dari database. Latensi jauh lebih rendah karena tidak ada round-trip ke server aplikasi.
Bagaimana ISR bekerja saat periode revalidate belum habis?
Selama periode revalidate belum habis, Next.js tetap menyajikan versi HTML yang di-cache tanpa memicu regenerasi. Setelah waktu revalidate berlalu, request pertama yang masuk akan memicu proses regenerasi di background — pengguna itu masih mendapat versi lama, tetapi request berikutnya sudah mendapat versi HTML yang baru.
Kesimpulan
SSR, SSG, dan ISR bukan pilihan yang bersaing — melainkan alat yang saling melengkapi. Next.js memberimu kebebasan untuk menggunakan strategi yang paling tepat di setiap halaman dalam satu project yang sama.
Ringkasnya:
- SSG → konten statis, performa maksimal, ideal untuk blog dan dokumentasi
- ISR → konten semi-dinamis, keseimbangan sempurna antara kecepatan dan kebaruan data
- SSR → konten real-time atau personal, cocok untuk dashboard dan fitur yang bergantung pada login
Semakin kamu memahami kebutuhan data di setiap halaman, semakin tepat pilihanmu — dan itu langsung berdampak pada performa, biaya server, dan pengalaman pengguna. Selamat bereksperimen, dan jangan ragu untuk eksplorasi artikel lainnya di KamusNgoding untuk terus mengasah kemampuanmu membangun aplikasi Next.js yang optimal!