Skip to main content
Skill web app · Pelajaran 5 dari 11 · 14 menit

Rendering Statis vs Dinamis di Next.js: Caching Dijelaskan

Pelajari rendering statis vs dinamis Next.js: generateStaticParams, revalidate, cache fetch dan route, default Next 15, cache header, dan kode nyata.

Oleh Matthews WongDitinjau pada

Poin utama

Route Next.js dirender secara statis, yaitu HTML yang dibuat lebih dulu saat build atau lewat revalidate dan dipakai bersama semua pengunjung, atau secara dinamis, yaitu HTML yang dibuat untuk setiap request. generateStaticParams menentukan path dinamis mana yang diprerender. Di Next.js 15, fetch dan route handler GET tidak lagi di-cache secara default.

Setelah pelajaran ini, Anda bisa

  • Membedakan route statis dan dinamis, serta apa yang mengubahnya.
  • Memprerender setiap halaman dari route dinamis dan menjaganya tetap segar dengan revalidate.
  • Menerapkan default caching Next.js 15 dan mengatur cache header dengan sengaja.
Lihat cara kerjanya

Ikuti diagramnya langkah demi langkah

Tekan Putar, atau melangkah dengan panah. Setiap langkah juga ditulis di bawah diagram.

Dari build sampai halaman segar

generateStaticParams memberi daftar path ke next build, yang menulis HTML untuk tiap path; pengunjung menerima halaman tersimpan itu, jendela revalidate memicu rebuild di latar belakang, dan path di luar daftar mengembalikan 404 saat dynamicParams bernilai false.

Ditinjau pada 17 Oktober 2026

Langkah 1 dari 6. Route dinamis seperti post blog punya satu template dan banyak path. Prerender berarti membuat HTML tiap path sebelum ada yang memintanya.

Tombol: ← → langkah · Home / End · K putar atau jeda

  1. Route dinamis seperti post blog punya satu template dan banyak path. Prerender berarti membuat HTML tiap path sebelum ada yang memintanya.

  2. Saat build, generateStaticParams berjalan lebih dulu dan mengembalikan array params. Di situs ini ia mengembalikan semua slug post, dan layout locale mengembalikan kedua locale.

    export function generateStaticParams()
  3. next build merender satu halaman per path dan menyimpan HTML-nya. Inilah rendering statis: pekerjaan dilakukan sekali, lebih dulu, dan hasilnya dipakai bersama oleh semua pengguna dan request.

  4. Pengunjung meminta post yang sudah diprerender. Server menjawab dengan HTML tersimpan alih-alih merender ulang, jadi biayanya dibayar saat build, bukan di tiap kunjungan.

    Tebak langkah berikutnya Jendela revalidate sudah lewat dan seorang pengunjung datang. Apa yang ia terima? (Menunggu sampai halaman baru dirender · Halaman lama, langsung; rebuild berjalan di belakangnya · 404)
  5. Setelah jendela revalidate, request berikutnya masih menerima halaman lama. Versi baru dibuat di latar belakang, dan request sesudahnya menerima halaman segar.

    export const revalidate = 21_600;Tebak langkah berikutnya dynamicParams bernilai false. Apa yang dikembalikan untuk path yang tidak ada di daftar? (Dirender saat diminta · 404 · Halaman tersimpan terdekat)
  6. Dengan dynamicParams false, path yang tidak ada di generateStaticParams mengembalikan 404. Default-nya, true, justru merendernya saat diminta.

    export const dynamicParams = false;

Apa beda rendering statis dan dinamis?

Pada rendering statis, HTML dibuat lebih dulu, baik saat build maupun lewat revalidate. Hasilnya di-cache dan dipakai bersama oleh semua pengguna dan request. Pada rendering dinamis, HTML dibuat saat request, sehingga halaman bisa bergantung pada siapa yang meminta. Statis adalah pilihan yang lebih murah untuk konten yang sama bagi semua orang, seperti artikel.

Baca selengkapnya (2)

Anda jarang memilih salah satunya secara manual. Next.js memutuskan dari apa yang disentuh kode. Menurut dokumentasi, komponen menjadi dinamis jika memakai cookies, headers, connection, draftMode, prop searchParams, unstable_noStore, atau fetch dengan cache bernilai no-store. Membaca salah satunya berarti hasilnya bergantung pada request, sehingga route tidak bisa diprerender.

Dua catatan praktis. Output next build mencantumkan strategi rendering setiap route, jadi bacalah alih-alih menebak. Dan saat development, halaman selalu dirender saat diminta dan tidak pernah di-cache, sehingga next dev tidak bisa menunjukkan perilaku caching. Jalankan next build dan next start untuk melihat perilaku produksi.

Bagaimana generateStaticParams memprerender route dinamis?

Segmen dinamis seperti [slug] adalah satu template dengan banyak kemungkinan path. generateStaticParams mengembalikan array objek, satu per path, dan Next.js menghasilkan route itu secara statis saat build, bukan saat diminta. Saat next build, fungsi ini berjalan sebelum layout dan page terkait dibuat, dan tidak dipanggil lagi saat revalidate.

Baca selengkapnya (3)

Situs ini memakainya dua kali. Layout blog mengembalikan semua slug di BLOG_META, dan layout locale mengembalikan dua locale. Karena segmen locale berada di atas segmen blog, setiap post diprerender sekali per locale. Itulah sebabnya biaya build bertambah seiring jumlah post, dan sebabnya satu kunjungan hampir tidak berbiaya.

dynamicParams menentukan nasib path yang tidak Anda daftarkan. Default-nya, true, membuatnya saat diminta. Jika false, hasilnya 404. Layout locale di sini mengaturnya ke false, dan komentarnya menjelaskan alasannya: request ke file gambar yang hilang bisa cocok dengan route blog dengan locale bernilai images, dan merender kombinasi itu berakhir sebagai 500. Sekarang hasilnya 404 yang bersih.

Anda harus selalu mengembalikan array, meski kosong. Array kosong merender setiap path saat pertama kali dikunjungi, sedangkan tidak mengembalikan apa pun membuat route menjadi dinamis.

Bagaimana revalidate menjaga halaman statis tetap segar?

Revalidate berbasis waktu bekerja dengan pola stale-while-revalidate. Ekspor angka revalidate dalam detik dari page, layout, atau route. Setelah waktu itu lewat, request berikutnya masih menerima halaman cache yang sudah usang. Cache diinvalidasi dan versi baru dibuat di latar belakang; begitu berhasil, request sesudahnya menerima halaman baru. Jika regenerasi gagal, halaman terakhir yang baik tetap disajikan dan Next.js mencoba lagi pada request berikutnya.

Baca selengkapnya (2)

Dokumentasi menyarankan nilai yang tinggi, misalnya satu jam, bukan satu detik. Jika butuh presisi, pakai revalidate on-demand: panggil revalidatePath atau revalidateTag dari Server Action atau Route Handler setelah data berubah. Jika butuh data real-time, pilih rendering dinamis. Halaman open-source di sini memakai jendela enam jam agar sejalan dengan fetcher datanya, sehingga halaman tidak membeku di waktu build.

Dua aturan penting. Revalidate terendah di antara layout, page, dan fetch pada sebuah route menentukan frekuensi untuk seluruh route. Dan nilainya harus bisa dianalisis secara statis: angka biasa berfungsi, tetapi ekspresi seperti 60 kali 10 tidak.

Apa yang berubah pada default caching di Next.js 15?

Next.js 15 membalik tiga default dari ter-cache menjadi tidak ter-cache. Permintaan fetch tidak di-cache secara default; aktifkan per request dengan cache bernilai force-cache. Route Handler GET tidak di-cache secara default; aktifkan dengan konfigurasi route dynamic bernilai force-static. Dan Router Cache di sisi klien kini punya stale time nol untuk segmen page, sehingga navigasi menampilkan data halaman terbaru.

Baca selengkapnya (2)

Sebagian perilaku tetap. Route metadata seperti sitemap.ts tetap statis kecuali memakai fungsi dinamis. Data layout bersama tidak diambil ulang saat navigasi, dan navigasi mundur serta maju tetap memulihkan dari cache. Perhatikan juga bahwa route yang berisi fetch tetap bisa diprerender dan HTML-nya di-cache; gunakan API connection jika Anda harus menjamin route bersifat dinamis.

Catatan versi: ini adalah dokumentasi Next.js 15, yang sesuai dengan situs ini. Next.js 16 memperkenalkan Cache Components di balik flag, dengan model caching yang berbeda, jadi periksa versi mayor yang dibahas sebuah tutorial sebelum menyalin konfigurasinya.

Di mana posisi header Cache-Control?

Cache render menentukan apakah server merender ulang. Cache-Control menentukan apakah browser atau CDN bertanya ke server sama sekali. Keduanya lapisan yang terpisah. Untuk halaman ISR, Next.js menambahkan cache-control yang tepat secara otomatis, dan sejak versi 15 ia tidak lagi menimpa nilai Cache-Control kustom dengan default miliknya, jadi header yang Anda atur di next.config adalah yang dikirim.

Baca selengkapnya (2)

Situs ini mengatur max-age satu tahun dengan immutable pada folder gambar dan chunk build-nya. MDN menjelaskan pasangannya: max-age menjaga respons tetap segar selama sekian detik, dan immutable berarti respons tidak akan berubah selama masih segar, cocok untuk URL yang membawa hash atau versi. Itulah sebabnya aturan chunk build hanya untuk produksi: nama chunk tetap sama di antara edit saat development, dan cache panjang di sana menyajikan kode usang.

Aturan praktisnya: panjang dan immutable untuk URL yang isinya tidak pernah berubah, dan pendek atau divalidasi ulang untuk URL yang isinya berubah. File di folder images yang Anda timpa dengan nama sama melanggar janji itu.

Cara situs ini melakukannya

Konfigurasi nyata di balik pelajaran ini

Kutipan dari repositori yang membangun dan men-deploy situs ini, apa adanya.

app/[locale]/(pages)/blog/[slug]/layout.tsx:169-173
// Prerender every blog post at build time (x2 locales via the root layout's
// generateStaticParams). Without this, all 300+ posts were rendered on demand.
export function generateStaticParams() {
  return Object.keys(BLOG_META).map((slug) => ({ slug }));
}

Satu baris mengubah registry menjadi daftar path. Komentarnya menyebut separuh lainnya: layout locale di atasnya menyediakan locale, sehingga tiap slug dibangun sekali per bahasa.

app/[locale]/layout.tsx:167-179
export function generateStaticParams() {
  return routing.locales.map((locale) => ({ locale }));
}

// Any `locale` outside generateStaticParams() 404s at the router, before a
// single segment renders. The middleware skips paths containing a dot so that
// public/ assets are served untouched, which means a request for a missing
// file like /images/blog/foo.jpg reaches the router and matches
// /[locale]/blog/[slug] with locale="images". Rendering that combination let
// next-intl read request headers during an on-demand static render, which
// Next.js rejects with "Page changed from static to dynamic at runtime" and
// serves as a 500. Rejecting unknown params up front makes it a clean 404.
export const dynamicParams = false;

dynamicParams false adalah pagar pengaman, bukan sekadar optimasi. Komentarnya mencatat kegagalan nyata: param tak dikenal sampai ke render saat diminta yang membaca header request, dan route statis tidak boleh melakukannya.

app/[locale]/(pages)/open-source/page.tsx:59-61
// ISR: the fetchers set a 6-hour revalidate, but pin the route too so the page
// itself is regenerated on the same cadence rather than frozen at build time.
export const revalidate = 21_600;

Fetch data membawa revalidate sendiri, dan route juga membawanya. Mengunci keduanya menjaga halaman dan datanya pada irama yang sama. Nilainya dalam detik, jadi ini enam jam.

app/llms.txt/route.ts:1-3
import { buildLlmsTxt } from "@/lib/llms"

export const dynamic = "force-static"

Route Handler GET tidak di-cache secara default di Next.js 15. Handler ini tidak punya input per request, jadi ia memilih kembali caching dengan force-static dan dibangun sekali seperti sebuah halaman.

next.config.ts:157-164
      {
        source: "/images/(.*)",
        headers: [
          {
            key: "Cache-Control",
            value: "public, max-age=31536000, immutable",
          }, // Cache images for 1 year
        ],

Header ini mengatur browser dan CDN, bukan rendering. Aman hanya karena file gambar dianggap tidak pernah diedit di tempat: gambar yang berubah mendapat nama file baru.

Yang sering rusak

Kesalahan yang benar-benar membuang waktu

  • Halaman yang Anda kira statis ternyata dinamis

    Satu pembacaan cookies, headers, atau searchParams membuat route dirender di setiap request. Baca output next build, temukan API yang disentuh route, lalu pindahkan ke komponen kecil, atau jauhkan dari route tersebut.

  • Hasil fetch tidak pernah di-cache setelah upgrade

    Next.js 15 tidak meng-cache fetch secara default. Jika halaman menjadi lebih lambat setelah upgrade dari 14, aktifkan pada pemanggilan dengan cache force-cache, atau atur waktu revalidate, jangan mengandalkan default lama.

  • Anda menguji revalidate di next dev dan tidak terjadi apa-apa

    Saat development, halaman selalu dirender saat diminta dan tidak pernah di-cache. Build dan jalankan server produksi untuk menguji ISR, dan harapkan request pertama setelah kedaluwarsa menerima halaman lama.

  • Path tak dikenal mengembalikan 500, bukan 404

    Dengan dynamicParams dibiarkan true, param tak terduga bisa sampai ke render saat diminta yang membutuhkan data request. Situs ini mengalaminya dengan URL gambar yang hilang. Atur dynamicParams ke false jika daftarnya sudah lengkap.

  • Immutable pada URL yang berubah

    Header immutable satu tahun membuat pengunjung menyimpan byte lama. Pakai hanya untuk file ber-hash atau yang tidak pernah diedit, dan beri nama baru pada file yang diganti.

Rendering statis, revalidate, dan dinamis berdampingan
StatisSaat buildKonten yang sama bagi semua oranggenerateStaticParams
Statis dengan revalidateSaat build, lalu di latar belakangKonten yang sesekali berubahrevalidate, revalidatePath, revalidateTag
DinamisPada setiap requestKonten yang bergantung pada requestcookies, headers, searchParams, no-store
Coba sendiri

Lakukan di komputer Anda sendiri

Coba hal ini di proyek Next.js 15 Anda sendiri, dengan server produksi, bukan next dev.

  1. Build dan baca daftar route. Temukan satu route dinamis dan perhatikan bagaimana path-nya dilaporkan.

    npm run build
  2. Tambahkan generateStaticParams ke route dinamis dengan dua id, build ulang, dan pastikan kedua path muncul di output.

  3. Atur dynamicParams ke false, jalankan server, lalu minta id yang tidak Anda daftarkan. Harapkan 404.

    npm start
  4. Baca header Cache-Control dari sebuah halaman dan dari file di folder yang Anda konfigurasi, lalu bandingkan.

    curl -sI http://localhost:3000/ | grep -i cache-control
Kuis singkat

Tiga pertanyaan sebelum lanjut

Pilih jawaban untuk melihat kenapa benar atau salah. Tidak ada yang disimpan atau dinilai, jadi tebak saja dengan bebas.

  1. 1.Mana yang membuat sebuah route dirender secara dinamis?
  2. 2.Jendela revalidate baru saja kedaluwarsa dan seorang pengunjung meminta halaman. Apa yang diterimanya?
  3. 3.Di Next.js 15, pemanggilan fetch biasa tanpa opsi:
Pikirkan dulu

Jawab dalam hati, lalu cek

  1. 1.Mengapa situs ini memprerender setiap post blog dua kali?Lihat jawabannya

    Jawaban:generateStaticParams di layout blog mengembalikan semua slug dan layout locale di atasnya mengembalikan kedua locale, sehingga tiap slug dibangun sekali per locale.

  2. 2.Apa yang diubah dynamicParams bernilai false?Lihat jawabannya

    Jawaban:Path yang tidak didaftarkan generateStaticParams mengembalikan 404, bukan dirender saat diminta.

  3. 3.Route handler untuk file yang tidak pernah berubah memerlukan pengaturan apa agar dibangun sekali?Lihat jawabannya

    Jawaban:export const dynamic = force-static, karena handler GET tidak di-cache secara default di Next.js 15.

Pelajari lebih dalam

Bahasan mendalam terkait di situs ini