Static Site Generation (SSG) di Next.js adalah metode render di mana page web dihasilkan di server-side pada waktu build, dan hasilnya disajikan sebagai static HTML kepada client.
Dalam Next.js, SSG dapat diimplementasikan menggunakan fungsi getStaticProps
, yang dieksekusi pada waktu build untuk mengambil data yang diperlukan dan menghasilkan static HTML. Halaman yang dihasilkan dengan SSG dapat dengan cepat di akses user karena hanya memerlukan pengiriman static HTML dari server tanpa proses rendering tambahan di sisi client. Berikut contoh implementasi getStaticProps
:
// pages/posts/[id].js
import React from 'react';
function Post({ carDetail }) {
// Render konten postingan
return (
<div>
<h1>{carDetail.name}</h1>
<p>{carDetail.bodyType}</p>
</div>
);
}
// Fungsi ini akan dipanggil saat build time
export async function getStaticProps({ params }) {
// Mengambil data postingan berdasarkan ID dari API
const res = await fetch(`https://auto2000.co.id/api/product/product-detail/${params.id}`);
const carDetail = await res.json();
// Kirim data sebagai props ke komponen halaman
return {
props: {
carDetail,
},
};
}
Jika path yang ingin di generate bergantung pada data eksternal ( API ) maka kita perlu menggunakan getStaticPath untuk menentukan path apa saja yang akan di generate. Berikut contoh penggunaannya :
// Fungsi ini menentukan path statis mana yang akan dihasilkan saat build time
export async function getStaticPaths() {
// Mengambil daftar postingan dari API
const res = await fetch('https://auto2000.co.id/api/product/products');
const cars = await res.json();
// Buat path statis berdasarkan ID postingan
const paths = cars.map((car) => ({
params: { id: car.id.toString() },
}));
// Return daftar path yang harus di-prerender berdasarkan data
return {
paths,
fallback: false, // Jika false, halaman untuk ID yang tidak ada akan menampilkan 404
};
}
Contoh implementasi :
Mari kita pakai skenario case pada Project Astra Auto2000. Auto2000 memiliki 30 mobil, setiap mobil memiliki page detail nya masing masing. Oleh karena itu, kita memerlukan 1 API yang bisa return 30 mobil tersebut. API tersebut akan di fetch di getStaticPaths untuk generate static path untuk page detail mobil. Berikut implementasi code nya :
import React from 'react';
function Post({ detailCar }) {
// Render konten postingan
return (
<div>
<h1>{detailCar.name}</h1>
<p>{detailCar.carType}</p>
</div>
);
}
// Fungsi ini menentukan path statis mana yang akan dihasilkan saat build time
export async function getStaticPaths() {
// Mengambil daftar postingan dari API
const res = await fetch('https://auto2000.co.id/api/product/products');
const cars = await res.json();
// Buat path statis berdasarkan ID postingan
const paths = cars.map((post) => ({
params: { id: post.id.toString() },
}));
// Return daftar path yang harus di-prerender berdasarkan data
return {
paths,
fallback: false, // Jika false, halaman untuk ID yang tidak ada akan menampilkan 404
};
}
// Fungsi ini akan dipanggil saat build time
export async function getStaticProps({ params }) {
// Mengambil data postingan berdasarkan ID dari API
const res = await fetch(`https://auto2000.co.id/api/product/product-detail/${params.id}`);
const detailCar = await res.json();
// Kirim data sebagai props ke komponen halaman
return {
props: {
detailCar,
},
};
}
Keuntungan utama SSG adalah performa yang sangat baik karena halaman sudah dirender sebelumnya dan caching di server atau CDN dapat dioptimalkan. Namun, SSG tidak cocok untuk page yang memerlukan update data secara real-time.
di Next.js memungkinkan halaman statis diperbarui secara otomatis setelah waktu tertentu ( interval ) tanpa memerlukan rebuild app. ISR menggabungkan benefit dari Static Site Generation (SSG) dengan kemampuan untuk memperbarui konten dinamis di server saat halaman diminta, menghasilkan performa yang baik dan data yang up-to-date.
Berikut contoh implementasi ISR :
import React from 'react';
function CarDetail({ detailCar }) {
return (
<div>
<h1>{detailCar.name}</h1>
<p>{detailCar.carType}</p>
</div>
);
}
export async function getStaticPaths() {
const res = await fetch('https://auto2000.co.id/api/product/products');
const cars = await res.json();
const paths = cars.map((car) => ({
params: { id: car.id.toString() },
}));
return {
paths,
fallback: false,
};
}
export async function getStaticProps({ params }) {
const res = await fetch(`https://auto2000.co.id/api/product/product-detail/${params.id}`);
const detailCar = await res.json();
return {
props: {
detailCar,
},
// Next.js akan meng-invalidasi cache ketika
// sebuah permintaan masuk, paling sering setiap 60 detik.
revalidate: 60,
};
}
Perhatikan bagian return pada function getStaticProps
, terdapat field line code revalidate: 60
. Itu menandakan bahwa page akan melakukan update apabila terdapat request dalam rentang waktu 60 detik. Berikut hal yang terjadi ketika proses revalidate terjadi :
next build
, semua page detail mobil ter-generate (ada 30 mobil pada case project Astra Auto2000)/mobil/detail/agya
) ter-cache.Selain ISR, terdapat satu approach lain yang dapat digunakan pada static pages, yaitu Revalidate on-demand. Pada approach ini, kita perlu membuat satu API Route yang dapat memicu proses revalidate untuk satu atau lebih path. Berikut contoh code untuk API Route nya :
// pages/api/revalidate.js
import { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
// Secret token checking
if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
return res.status(401).json({ message: 'Invalid token' })
}
try {
// memicu proses revalidate untuk page detail mobil dengan carCode
// yang dikirim melalui query
await res.revalidate(`/mobil/detail/${req.query.carCode}`) // bisa memicu lebih dari 1 kali res.revalidate dalam 1 waktu
return res.json({ revalidated: true })
} catch (err) {
// Jika terdapat error dalam proses on demand revalidation, Next.js
// akan tetap menampilkan stale page atau page yang terakhir
// kali sukses di generate
return res.status(500).send('Error revalidating')
}
}
pada getStaticPaths
, kita bisa mengembalikan fallback sebagai return values. Terdapat 3 values yang bisa kita kirim sebagai fallback. Berikut 3 valuesnya :
fallback : false
ketika kita mengembalikan false sebagai value fallback, maka semua path tidak di return di getStaticPaths
akan menampilkan page 404. Fallback false dapat digunakan untuk page yang jarang bertambah.
fallback : true
ketika kita mengembalikan true sebagai value fallback, maka path yang tidak termasuk di return getStaticPaths
tidak akan menampilkan page 404, melainkan akan menampilkan fallback page yang bisa kita atur tampilannya. Berikut contoh implementasi fallback : true
:
import { useRouter } from 'next/router';
const CarDetail = ({ carDetail }) => {
const router = useRouter();
// Show a loading state while the page is being generated
if (router.isFallback) {
return <div>Loading detail page {carDetail.name}...</div>;
}
// Render the post content
return (
<div>
<h1>{carDetail.name}</h1>
<p>{carDetail.bodyType}</p>
</div>
);
};
export async function getStaticPaths() {
const res = await fetch('https://auto2000.co.id/api/product/products');
const cars = await res.json();
const paths = cars.map((car) => ({
params: { id: car.id.toString() },
}));
return {
paths,
fallback: true,
};
}
export async function getStaticProps({ params }) {
const { id } = params;
try {
// Fetch the data for the specific post
const res = await fetch(`https://auto2000.co.id/api/product/product-detail/${params.id}`);
const carDetail = await res.json();
// Return the post data
return {
props: {
carDetail
},
};
} catch (error) {
return {
notFound: true // Return 404 jika data detail car tidak ditemukan
};
}
}
export default CarDetail;
fallback : ‘blocking’
ketika kita mengembalikan “blocking”
sebagai value fallback, maka path yang tidak termasuk di return getStaticPaths
tidak akan menampilkan page 404, melainkan akan menunggu HTML di generate, behaviour nya sama seperti SSR, namun page yang di generate akan menjadi cached untuk request-request selanjutnya. fallback : ‘blocking’
tidak akan menampilkan fallback page seperti fallback : true
.Berikut contoh implementasi fallback : "blocking"
:
const CarDetail = ({ carDetail }) => {
// Render the post content
return (
<div>
<h1>{carDetail.name}</h1>
<p>{carDetail.bodyType}</p>
</div>
);
};
export async function getStaticPaths() {
const res = await fetch('https://auto2000.co.id/api/product/products');
const cars = await res.json();
const paths = cars.map((car) => ({
params: { id: car.id.toString() },
}));
return {
paths,
fallback: 'blocking',
};
}
export async function getStaticProps({ params }) {
const { id } = params;
try {
// Fetch the data for the specific post
const res = await fetch(`https://auto2000.co.id/api/product/product-detail/${params.id}`);
const carDetail = await res.json();
// Return the post data
return {
props: {
carDetail
},
};
} catch (error) {
return {
notFound: true // Return 404 jika data detail car tidak ditemukan
};
}
}
export default CarDetail;
Reference :
SSG : https://nextjs.org/docs/pages/building-your-application/rendering/static-site-generation
getStaticPaths : https://nextjs.org/docs/pages/api-reference/functions/get-static-paths#getstaticpaths-return-values