Cara Menggunakan SSR dan SSG di Next.js
Salah satu keunggulan utama Next.js dibanding React biasa adalah kemampuannya melakukan rendering di sisi server. Cara menggunakan SSR dan SSG di Next.js menjadi pengetahuan wajib bagi developer yang ingin membangun aplikasi web cepat dan SEO-friendly. Artikel ini membahas keduanya secara praktis dengan contoh kode yang bisa langsung kamu terapkan.
Apa Itu SSR dan SSG?
Sebelum masuk ke implementasi, mari pahami dulu konsep dasarnya.
Server-Side Rendering (SSR)
SSR berarti halaman di-render di server setiap kali ada request. Server mengambil data, membangun HTML lengkap, lalu mengirimkannya ke browser. Cocok untuk halaman yang datanya sering berubah, misalnya dashboard atau halaman profil pengguna.
Static Site Generation (SSG)
SSG berarti halaman di-generate saat build time. HTML sudah jadi sebelum ada request dari user. Hasilnya bisa di-cache di CDN, sehingga sangat cepat. Cocok untuk halaman yang jarang berubah seperti blog, dokumentasi, atau landing page.
Kapan Menggunakan SSR vs SSG?
Pemilihan antara SSR dan SSG bergantung pada karakteristik data dan kebutuhan aplikasi kamu.
Gunakan SSG Ketika
- Konten tidak berubah setiap request (blog, about page, FAQ)
- Kamu butuh performa maksimal dan waktu muat tercepat
- Halaman bisa di-cache di CDN secara global
- Data bisa di-fetch saat build dan tetap relevan untuk waktu tertentu
Gunakan SSR Ketika
- Data berubah setiap detik atau tiap request (harga saham, feed real-time)
- Konten bergantung pada cookies, session, atau parameter user
- Kamu butuh SEO tapi data terlalu dinamis untuk SSG
- Halaman memerlukan autentikasi sebelum render
Implementasi SSG dengan getStaticProps
Di Next.js (Pages Router), SSG diimplementasikan menggunakan fungsi getStaticProps. Fungsi ini dijalankan saat build time dan hasilnya dikirim sebagai props ke komponen halaman.
// pages/blog/index.js
export async function getStaticProps() {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return {
props: {
posts,
},
revalidate: 3600, // ISR: regenerate tiap 1 jam
};
}
export default function BlogPage({ posts }) {
return (
<div>
<h1>Blog</h1>
{posts.map((post) => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
))}
</div>
);
}
Perhatikan properti revalidate. Ini adalah fitur Incremental Static Regeneration (ISR) yang memungkinkan halaman di-regenerate di background setelah interval tertentu. Jadi kamu dapat keuntungan SSG (cepat) sekaligus data yang relatif fresh.
SSG untuk Halaman Dinamis
Untuk halaman dengan parameter dinamis seperti /blog/[slug], kamu perlu menambahkan getStaticPaths agar Next.js tahu halaman mana saja yang harus di-generate.
// pages/blog/[slug].js
export async function getStaticPaths() {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
const paths = posts.map((post) => ({
params: { slug: post.slug },
}));
return {
paths,
fallback: 'blocking', // render di server jika belum ada
};
}
export async function getStaticProps({ params }) {
const res = await fetch(
`https://api.example.com/posts/${params.slug}`
);
const post = await res.json();
if (!post) {
return { notFound: true };
}
return {
props: { post },
revalidate: 3600,
};
}
export default function BlogPost({ post }) {
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
);
}
Opsi fallback: 'blocking' penting di sini. Ketika user mengakses halaman yang belum di-generate saat build, Next.js akan melakukan SSR sekali, menyimpan hasilnya, lalu menyajikan versi statis untuk request berikutnya.
Implementasi SSR dengan getServerSideProps
Untuk SSR, gunakan getServerSideProps. Fungsi ini dijalankan setiap ada request, bukan saat build.
// pages/dashboard.js
export async function getServerSideProps(context) {
const { req } = context;
const token = req.cookies.auth_token;
if (!token) {
return {
redirect: {
destination: '/login',
permanent: false,
},
};
}
const res = await fetch('https://api.example.com/dashboard', {
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();
return {
props: { data },
};
}
export default function Dashboard({ data }) {
return (
<div>
<h1>Dashboard</h1>
<p>Total Order: {data.totalOrders}</p>
<p>Revenue: Rp {data.revenue.toLocaleString('id-ID')}</p>
</div>
);
}
Perhatikan bahwa getServerSideProps menerima context yang berisi informasi request seperti cookies, query parameters, dan headers. Ini yang membuat SSR cocok untuk halaman yang butuh autentikasi.
Pendekatan di App Router (Next.js 13+)
Jika kamu menggunakan App Router (direktori app/), konsep SSR dan SSG tetap ada tapi implementasinya berbeda. Secara default, semua komponen di App Router adalah React Server Components.
Static Rendering (Default)
// app/blog/page.js
async function getPosts() {
const res = await fetch('https://api.example.com/posts', {
cache: 'force-cache', // default: static
});
return res.json();
}
export default async function BlogPage() {
const posts = await getPosts();
return (
<div>
<h1>Blog</h1>
{posts.map((post) => (
<article key={post.id}>
<h2>{post.title}</h2>
</article>
))}
</div>
);
}
Dynamic Rendering (SSR)
// app/dashboard/page.js
import { cookies } from 'next/headers';
export const dynamic = 'force-dynamic'; // paksa SSR
export default async function Dashboard() {
const cookieStore = cookies();
const token = cookieStore.get('auth_token');
const res = await fetch('https://api.example.com/dashboard', {
headers: { Authorization: `Bearer ${token?.value}` },
cache: 'no-store', // jangan cache
});
const data = await res.json();
return (
<div>
<h1>Dashboard</h1>
<p>Total Order: {data.totalOrders}</p>
</div>
);
}
Di App Router, kamu mengontrol rendering lewat opsi cache di fetch atau export dynamic. Ini lebih intuitif karena tidak perlu fungsi khusus seperti getStaticProps.
Tips Optimasi Performa
Berikut beberapa tips praktis untuk memaksimalkan performa SSR dan SSG di Next.js.
Gunakan ISR untuk Keseimbangan
ISR adalah sweet spot antara SSG dan SSR. Kamu mendapat halaman statis yang cepat, tapi tetap bisa update data secara berkala. Set revalidate sesuai seberapa sering data berubah.
Hindari SSR untuk Semua Halaman
SSR menambah beban server karena setiap request harus diproses. Gunakan SSR hanya untuk halaman yang benar-benar membutuhkannya. Halaman statis seperti about, contact, atau blog post sebaiknya tetap SSG.
Cache API Response
Jika menggunakan SSR, pastikan API backend kamu sudah di-cache dengan baik. Gunakan Redis atau in-memory cache agar response time dari API tidak memperlambat SSR.
// Contoh header cache di API response
res.setHeader(
'Cache-Control',
'public, s-maxage=60, stale-while-revalidate=300'
);
Manfaatkan next/image
Komponen next/image otomatis mengoptimasi gambar dengan lazy loading, format WebP, dan ukuran responsif. Ini berlaku baik di halaman SSR maupun SSG.
import Image from 'next/image';
<Image
src="/hero.jpg"
alt="Hero image"
width={800}
height={400}
priority // load segera untuk gambar above-the-fold
/>
Kesalahan Umum yang Harus Dihindari
Ada beberapa kesalahan yang sering dilakukan developer saat mengimplementasikan SSR dan SSG.
Pertama, menggunakan SSR padahal SSG cukup. Ini membuang resource server tanpa keuntungan nyata. Evaluasi dulu apakah data benar-benar perlu fresh setiap request.
Kedua, lupa menangani error di getServerSideProps. Jika API gagal, halaman akan error 500. Selalu wrap dengan try-catch dan sediakan fallback.
Ketiga, tidak memanfaatkan ISR. Banyak developer langsung loncat ke SSR padahal ISR dengan revalidate sudah cukup untuk kebanyakan kasus.
Kesimpulan
SSR dan SSG di Next.js masing-masing punya kelebihan. SSG memberikan performa terbaik untuk konten statis, sementara SSR cocok untuk data yang sangat dinamis atau butuh autentikasi. Dengan ISR, kamu bisa mendapatkan keduanya.
Mulailah dengan SSG sebagai default, tambahkan ISR jika butuh data yang lebih fresh, dan gunakan SSR hanya ketika benar-benar diperlukan. Pendekatan ini akan menghasilkan aplikasi Next.js yang cepat, SEO-friendly, dan efisien dari sisi server.