# Streaming dan Suspense di Next.js: Loading UI yang Terasa Cepat

> Gunakan streaming SSR dan React Suspense di Next.js untuk membuat halaman terasa instan, dengan loading.tsx, boundary granular, dan parallel data fetching yang menghilangkan request waterfall.

URL: https://www.matthewswong.com/id/blog/nextjs-streaming-suspense-patterns/

- Author: Matthews Wong (https://www.matthewswong.com/id/about/)
- Published: 2026-07-07
- Updated: 2026-07-07
- Category: Web Dev

Server rendering tradisional bersifat semua atau tidak sama sekali. Server mengumpulkan setiap data yang dibutuhkan halaman, menyusun seluruh dokumen HTML, dan baru setelah itu mengirim satu respons ke browser. Jika satu query lambat, seluruh halaman menunggu di belakangnya, dan pengunjung menatap tab kosong sambil bertanya-tanya apakah ada sesuatu yang sedang terjadi.

Streaming server rendering yang dipadukan dengan React Suspense membalikkan model tersebut. Server mengirim bagian halaman yang sudah siap segera, lalu mengalirkan sisanya begitu setiap bagian selesai diproses, dalam respons HTTP yang sama. Total beban kerja server tidak berkurang, tetapi halaman terasa jauh lebih cepat karena pengguna melihat sesuatu yang bermakna dalam hitungan milidetik, bukan menunggu query paling lambat selesai.

Cara Lama: Satu Render Besar yang Memblokir

Pada pendekatan klasik fetch lalu render, komponen halaman menunggu setiap panggilan data selesai sebelum mengembalikan markup apa pun. Framework tidak bisa mengirim satu byte pun dari respons sampai setiap promise dalam fungsi tersebut selesai, yang berarti dependensi paling lambat menentukan time to first byte untuk seluruh rute, bukan hanya bagian yang membutuhkannya.

Pengguna mengalami ini sebagai layar putih kosong yang diikuti kemunculan tiba-tiba halaman yang sudah jadi. Meski total waktu pemrosesan wajar, tidak adanya progres yang terlihat terasa seperti sistem yang rusak atau lambat, dan pada koneksi seluler yang lambat, keheningan itu memanjang dan memperparah frustrasi.

loading.tsx: Suspense Otomatis Per Segmen Rute

Next.js memberikan setiap segmen rute cara ringan untuk mengaktifkan streaming tanpa menyentuh satu pun elemen Suspense secara langsung. Letakkan file loading di samping file page pada App Router, dan framework otomatis menyisipkannya di dalam layout, membungkus page beserta layout bersarang di bawahnya dalam sebuah boundary yang menampilkan fallback begitu navigasi dimulai.

Karena fallback sering sudah di-prefetch sebelumnya, navigasi terasa instan bahkan sebelum rute tujuan selesai dirender di server. Isi respons sendiri mulai mengalir tepat saat fallback dirender atau komponen di bawahnya menunda diri, dan server tetap mengembalikan status kode sukses biasa, karena header sudah dikirim sebelum bagian yang lambat selesai.

- Jaga fallback tetap ringan, skeleton atau versi sebagian dari tampilan akhir bekerja jauh lebih baik dibanding spinner polos yang menutupi seluruh layar
- File loading membungkus page dan layout bersarang di bawahnya, tetapi tidak membungkus file template atau error yang sejajar dalam segmen yang sama
- Jika sebuah layout membaca cookies, headers, atau data lain yang tidak di-cache di luar boundary-nya sendiri, navigasi tetap terblokir olehnya, jadi pindahkan pengambilan data itu ke page atau beri boundary tersendiri
- Mesin pencari yang tidak dapat menjalankan JavaScript tetap menerima metadata halaman yang sudah selesai diproses di dalam head dokumen sebelum streaming dimulai, jadi teknik ini tidak merugikan sisi SEO sama sekali

Boundary Suspense Granular Lebih Baik daripada Satu Spinner Besar

Satu file loading mencakup seluruh rute, yang merupakan titik awal yang baik tetapi menjadi alat yang terlalu kasar begitu sebuah halaman memiliki beberapa bagian independen. Header, widget komentar, dan panel rekomendasi tidak saling bergantung, jadi membungkus setiap bagian dalam boundary-nya sendiri membuat bagian yang cepat langsung dirender sementara hanya bagian yang lambat menampilkan fallback.

Boundary juga bisa disusun bersarang untuk pengungkapan progresif: boundary luar menampilkan fallback besar sampai konten utama siap, dan boundary dalam di sekitar sub bagian yang lebih lambat tetap menampilkan fallback kecilnya sendiri setelah konten luar muncul. Boundary yang selesai dalam rentang waktu yang berdekatan cenderung muncul bersamaan daripada muncul satu per satu, sehingga halaman tidak terasa patah-patah.

Menghilangkan Request Waterfall dengan Parallel Fetching

Request waterfall terjadi ketika panggilan jaringan yang tidak saling berkaitan berjalan satu demi satu murni karena cara kode ditulis, bukan karena salah satu hasil benar-benar bergantung pada yang lain. Menunggu satu panggilan lalu memulai panggilan berikutnya di dalam fungsi async yang sama membuat kedua perjalanan bolak-balik jaringan itu berurutan, padahal kedua data tersebut tidak ada hubungannya sama sekali.

Solusinya adalah memulai kedua panggilan sebelum menunggu salah satunya, lalu menunggu keduanya bersamaan, sehingga keduanya berjalan bersamaan bukan bergiliran. Satu catatan penting: jika setiap request harus berhasil agar halaman berguna, gunakan kombinator ketat yang gagal total jika salah satu gagal; jika hasil sebagian masih bisa diterima, gunakan versi yang menyelesaikan setiap promise secara individual dan tidak pernah menolak seluruh batch. Dan ketika sebuah fetch memang membutuhkan hasil dari fetch sebelumnya, simpan bagian yang bergantung itu dalam boundary-nya sendiri agar tidak menahan bagian halaman lain yang tidak perlu menunggu.

Pola

Apa yang Terjadi

Kapan Digunakan

|  |  |  |
| --- | --- | --- |
| Await berurutan | Request kedua tidak bisa dimulai sampai request pertama selesai | Request kedua benar-benar membutuhkan data yang dihasilkan request pertama |
| Kombinator promise bersamaan | Request independen dimulai bersamaan dan seluruh grup selesai secepat request yang paling lambat | Request sama sekali tidak bergantung satu sama lain |
| Satu boundary per bagian | Sepotong UI independen mengalir masuk dengan waktunya sendiri, terpisah dari bagian halaman lainnya | Satu bagian lambat yang tidak boleh menahan bagian lain untuk muncul |

Streaming Client Component dengan Hook use

Sebuah server component dapat memanggil fungsi data async tanpa menunggunya, lalu menyerahkan promise yang belum selesai itu langsung ke client component sebagai prop, membungkus komponen penerima dalam boundary Suspense. Di dalam client component, hook use milik React membaca nilai akhir dari promise tersebut, dan boundary di sekitarnya otomatis menampilkan fallback sampai promise selesai.

Ini adalah model mental yang cukup berbeda dari mengambil data di dalam effect hook. Effect hanya berjalan setelah komponen selesai dipasang, sehingga tampilan pertama selalu kosong untuk bagian itu. Membaca promise dengan hook use terintegrasi langsung dengan Suspense, sehingga fallback muncul segera dan boundary berganti otomatis begitu data asli siap, tanpa perlu melacak status loading secara manual.

> Rancang setiap fallback agar menempati ruang yang kurang lebih sama dengan konten yang akan menggantikannya. Cadangkan rasio aspek gambar, beri skeleton tinggi baris yang sama dengan teks aslinya, dan pergantian akan terasa seperti pengungkapan yang mulus, bukan lompatan tata letak yang mengejutkan.

> Beberapa browser berbasis WebKit menahan awal respons streaming sampai melewati ambang ukuran tertentu, sehingga halaman demo kecil bisa terlihat termuat sekaligus bukan secara progresif. Ini adalah perilaku bawaan browser, bukan server yang rusak, jadi selalu nilai perilaku streaming pada halaman nyata dengan payload yang realistis, bukan contoh hello world yang sepele.

Checklist Sebelum Dirilis

- Beri setiap bagian yang benar-benar lambat boundary-nya sendiri daripada mengandalkan satu file loading untuk seluruh rute
- Jalankan pemanggilan data independen melalui kombinator bersamaan, bukan merangkai pernyataan await secara berurutan
- Hanya menyusun boundary bersarang ketika data di dalamnya memang bergantung pada hasil boundary luar yang selesai lebih dulu
- Jaga setiap fallback tetap mendekati dimensi konten akhir agar tidak ada yang melompat begitu data asli tiba
- Uji pada profil jaringan yang dibatasi, karena server pengembangan lokal yang cepat menyembunyikan kerusakan akibat waterfall yang baru terlihat setelah ada latensi nyata

Streaming dan Suspense tidak membuat server bekerja lebih sedikit, keduanya mengubah metrik mana yang paling penting. Total waktu muat berhenti menjadi satu-satunya angka yang layak dioptimalkan, dan waktu muat yang dirasakan, seberapa cepat pengguna melihat sesuatu yang bermakna, menjadi hal yang sebenarnya sedang dikejar.

Gabungkan boundary otomatis yang diberikan loading.tsx secara gratis dengan Suspense yang diletakkan manual di sekitar bagian yang benar-benar lambat, ambil data independen secara bersamaan, dan ukur hasilnya pada koneksi yang dibatasi sebelum menganggapnya selesai. Kombinasi itulah yang mengubah halaman yang secara teknis benar menjadi halaman yang terasa cepat.

## Sumber dan referensi lebih lanjut:

- Dokumentasi Next.js — konvensi file loading.js
- Dokumentasi React — Suspense
- Dokumentasi Next.js — Fetching Data, pola streaming, parallel, dan sequential

> **Key takeaway:** Streaming server rendering dengan React Suspense memungkinkan Next.js langsung mengirim bagian halaman yang sudah siap dan menyusulkan bagian yang lebih lambat begitu datanya selesai diambil, menggantikan satu render yang memblokir semuanya dengan boundary per bagian, pengambilan data paralel, dan hook use, sehingga waktu muat yang dirasakan membaik tanpa mengurangi total beban kerja server.

## Source links

- https://nextjs.org/docs/app/api-reference/file-conventions/loading
- https://react.dev/reference/react/Suspense
- https://nextjs.org/docs/app/getting-started/fetching-data

## FAQ

### Apa itu streaming SSR di Next.js dan apa bedanya dengan server rendering biasa?

Streaming SSR mengirim HTML sebuah halaman dalam beberapa bagian melalui satu respons, bukan menunggu semua data selesai sebelum mengirim apa pun. Server rendering biasa memblokir seluruh respons pada pengambilan data yang paling lambat, sementara streaming membuat konten yang cepat muncul segera dan bagian yang lebih lambat mengisi setelahnya. Total beban kerja server relatif sama, tetapi waktu muat yang dirasakan membaik karena pengguna langsung melihat sesuatu yang bermakna.

### Mengapa menambahkan file loading.tsx membuat halaman Next.js saya terasa lebih cepat?

File loading.tsx otomatis dibungkus di sekitar page dan layout bersarangnya dalam sebuah boundary Suspense, sehingga Next.js menampilkan fallback tersebut tepat saat navigasi dimulai, bukan membiarkan layar kosong. Fallback ini sering sudah di-prefetch sebelumnya, sehingga transisi terasa instan. Ini tidak mengurangi waktu pengambilan data yang sebenarnya, tetapi menghilangkan keheningan di mana pengguna tidak melihat apa pun terjadi.

### Bagaimana cara menghindari request waterfall saat mengambil data di Server Component Next.js?

Mulai setiap panggilan fetch independen sebelum menunggu salah satunya, lalu tunggu semua promise yang dihasilkan bersamaan dengan kombinator bersamaan seperti Promise.all, bukan menulis pernyataan await berurutan satu demi satu. Await berurutan memaksa panggilan jaringan yang tidak berkaitan berjalan bergiliran padahal tidak ada hasil yang bergantung pada yang lain. Hanya pertahankan fetch berurutan ketika memang membutuhkan data dari request sebelumnya.

### Bisakah saya menggunakan beberapa boundary Suspense pada halaman yang sama?

Bisa, dan biasanya ini adalah pola yang lebih baik begitu sebuah halaman memiliki lebih dari satu bagian independen. Membungkus header, widget komentar, dan panel rekomendasi dalam boundary Suspense yang terpisah membuat setiap bagian mengalir masuk dengan waktunya sendiri, sehingga satu bagian yang lambat tidak menahan bagian lain untuk muncul. Boundary juga bisa disusun bersarang untuk pengungkapan progresif, menampilkan fallback besar dahulu lalu fallback kecil untuk sub bagian yang lebih lambat.

### Apa itu hook use dan bagaimana hubungannya dengan streaming data ke Client Component?

Hook use milik React memungkinkan Client Component membaca nilai akhir dari sebuah promise yang dibuat di Server Component dan diteruskan sebagai prop tanpa ditunggu terlebih dahulu. Membungkus komponen penerima dalam boundary Suspense membuat fallback muncul otomatis selama promise masih tertunda dan berganti ke konten asli begitu selesai. Ini berbeda dari mengambil data di dalam effect hook, karena pengambilan data berbasis effect tidak pernah memicu fallback Suspense pada tampilan pertama.

---

- HTML version: https://www.matthewswong.com/id/blog/nextjs-streaming-suspense-patterns/
- English: https://www.matthewswong.com/en/blog/nextjs-streaming-suspense-patterns/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
