Kenapa Build Next.js Kamu Lama Banget Cek Setup Server Component Kamu Sekarang

Kenapa Build Next.js Kamu Lama Banget Cek Setup Server Component Kamu Sekarang
Photo by James Wiseman/Unsplash

Next.js itu powerful, nggak ada yang bantah. Sejak App Router dan React Server Components (RSC) dikenalkan, developer experience (DX) kita naik kelas. Tapi ada satu pain point yang rasanya bikin kepala pusing: kenapa proses next build jadi lamaaa banget, kayak nunggu giliran cukur rambut di tanggal tua?

Kalau kamu sudah migrasi ke App Router dan masih merasakan build time yang membengkak, kemungkinan besar masalahnya bukan pada Next.js-nya, tapi pada setup Server Component kamu. Kita sering dengar Next.js 14/15 itu cepat, tapi kalau implementasinya nggak optimal, jangankan cepat, loading bar pas build bisa bikin kamu memutuskan pindah profesi jadi petani lele.

Kita di Javapixa Creative Studio sering banget menangani kasus project yang punya build time parah. Ini bukan hanya tentang kecepatan deploy, tapi juga tentang biaya hosting (terutama di Vercel atau layanan lain) dan yang paling penting, developer velocity. Mari kita bongkar tuntas, kenapa Server Component yang seharusnya jadi solusi, malah jadi biang keladi kelambatan, dan bagaimana cara membetulkannya.

---

Miskonsepsi Fatal: Kenali Batasan Server Component

Dulu, di Pages Router, hidup kita lumayan jelas: kita pakai getServerSideProps atau getStaticProps, sisanya Client Side Rendering (CSR). Sekarang, di App Router, hampir semua komponen secara default adalah Server Component (SC). Ini adalah inti dari kecepatan Next.js baru, karena proses rendering dilakukan di server, mengurangi bundle size yang harus dikirim ke browser.

Tapi, SC punya batasan. SC tidak bisa menggunakan hooks seperti useState, useEffect, atau useContext. SC juga tidak bisa merender elemen yang memerlukan interaksi browser seperti event handlers (misalnya onClick). Untuk kasus ini, kita wajib menggunakan Client Component (CC) dengan menambahkan direktif "use client" di baris paling atas file komponen.

Masalahnya muncul ketika kita salah menentukan batas.

1. Kesalahan Klasik: "Over-Clienting"

Ini adalah penyakit paling umum. Demi cepat, atau karena kebiasaan lama, banyak developer yang meletakkan "use client" terlalu tinggi di component tree.

Skenario Buruk: Kamu punya Sidebar.tsx. Di dalamnya ada tombol toggle yang perlu useState. Daripada memisahkan tombol toggle itu menjadi CC sendiri, kamu langsung menaruh "use client" di Sidebar.tsx yang ukurannya masif (misalnya 500 baris kode dan banyak dependensi).

Dampaknya pada Build Time: Ketika sebuah komponen ditandai sebagai CC, Next.js dan Webpack harus melakukan lebih banyak pekerjaan:

Hydration Overhead: Seluruh code CC tersebut harus dibundel, dikirim ke browser, dan kemudian proses hydration (penghidupan kembali state dan event listeners) harus dilakukan. Jika Sidebar itu besar, hydration cost-nya tinggi, memperlambat initial load dan build time karena proses bundling* jadi lebih kompleks. Tree Shaking yang Terganggu: Jika kamu meletakkan CC terlalu tinggi, semua dependensi yang di-import oleh komponen tersebut (meskipun hanya digunakan oleh bagian SC lain) berisiko terlewatkan dari tree shaking yang optimal, atau malah ikut masuk ke client bundle* padahal tidak diperlukan di sana.

Tips Javapixa: Strategic Boundary Placement Prinsipnya: Push the "use client" boundary as far down the tree as possible.

Kalau cuma tombol toggle yang butuh state, jadikan tombol itu saja yang CC. Biarkan layout utama Sidebar tetap menjadi SC. SC akan merender markup statisnya dengan cepat, dan hanya code minimal (tombol toggle tersebut) yang dikirim ke client untuk hydration. Ini sangat signifikan mengurangi ukuran client bundle dan mempercepat proses build serta loading di browser.

2. Import Maut: Server Components Mengimpor Client Dependencies

Server Components dieksekusi di lingkungan Node.js di server. Mereka tidak bisa mengakses API browser (seperti window atau localStorage).

Tapi, tahukah kamu bahwa Server Component juga tidak boleh mengimpor library yang hanya ditujukan untuk client side?

Misalnya, kamu meng-import sebuah package besar yang spesifik untuk browser (misalnya, package visualisasi data yang butuh akses DOM) ke dalam Server Component. Meskipun kamu tidak menggunakannya secara langsung di SC itu, proses bundling Next.js akan bingung dan bisa jadi malah memasukkan package itu ke dalam build server, atau lebih parahnya, memicu error yang memperlambat build karena adanya mismatch target runtime.

Selalu pastikan dependensi yang kamu import di Server Component adalah dependensi server-safe atau universal.

---

Server Components dan Data Fetching: Jebakan Water-falling

Salah satu daya tarik utama SC adalah kemampuan native untuk data fetching tanpa harus repot dengan useEffect atau library pihak ketiga seperti SWR atau React Query (meskipun library ini masih relevan untuk CC). Kamu cukup menggunakan await pada fungsi async di Server Component.

Namun, cara kamu menyusun panggilan data fetching adalah faktor terbesar yang menentukan kecepatan build kamu.

3. Cascading Data Fetching (The Water-fall)

Water-falling terjadi ketika panggilan data fetching kedua baru bisa dieksekusi setelah panggilan data fetching pertama selesai.

Contoh (Bad Practice):

typescript
// Component: PostDetail.tsx (SC)
async function PostDetail({ postId }) {
  // Panggilan 1: Harus selesai dulu
  const post = await db.posts.get(postId);// Panggilan 2: Baru bisa dimulai setelah Panggilan 1 selesai
  const comments = await db.comments.getForPost(post.id);

Dalam skenario di atas, Next.js harus menunggu total waktu dari Panggilan 1 + Panggilan 2. Jika setiap panggilan butuh 200ms, total waktu rendering dan build untuk komponen ini adalah 400ms. Kalikan ini dengan puluhan atau ratusan komponen di page besar, dan kamu dapat build time yang lamanya minta ampun.

Tips Javapixa: Parallel Fetching adalah Kunci Build Cepat

Jika Panggilan 1 dan Panggilan 2 tidak memiliki dependensi satu sama lain (atau hanya membutuhkan ID yang sudah tersedia), lakukan fetching secara paralel menggunakan Promise.all().

Contoh (Good Practice):

typescript
// Component: PostDetail.tsx (SC)
async function PostDetail({ postId }) {
  // Keduanya dimulai secara bersamaan
  const [post, comments] = await Promise.all([
    db.posts.get(postId),
    db.comments.getForPost(postId) // Asumsi postId sudah tersedia
  ]);

Dengan Promise.all(), total waktu tunggu hanya sama dengan waktu operasi terlama (misalnya 250ms), bukan penjumlahan keduanya. Menggunakan pola ini secara konsisten di seluruh aplikasi akan memangkas build time hingga 30-50% karena server rendering bisa selesai jauh lebih cepat.

4. Cache yang Terlupakan: Default Fetch Caching

Next.js (terutama yang menggunakan built-in fetch API) secara default akan menerapkan cache.

Static Pages/SSG-like: Jika kamu menggunakan fetch di Server Component dan tidak mengatur opsi cache apapun, Next.js akan menganggap hasil fetch itu statis dan me-cache-nya pada saat build time*.

Ini bagus untuk page yang jarang berubah. Tapi, kalau kamu memanggil API yang sering update tanpa sengaja menonaktifkan cache dengan benar (misalnya, lupa menambahkan revalidate: 60), Next.js mungkin akan terus menggunakan data lama dari cache build time, atau malah mencoba mengambil data live setiap kali build (jika cache benar-benar dinonaktifkan), yang tentu saja memperlambat total durasi next build.

Solusi: Pastikan setiap panggilan fetch di SC memiliki cache strategy yang jelas.

Untuk data dinamis (selalu live*): fetch(url, { cache: 'no-store' }) Untuk data yang perlu di-revalidate* berkala (ISR-like): fetch(url, { next: { revalidate: 3600 } })

Pemahaman yang solid tentang data fetching dan caching di App Router bukan lagi opsional, itu wajib.

---

Optimasi Tingkat Lanjut: Build Analysis dan Server Actions

Jika masalah water-falling dan boundary sudah kamu bereskan tapi build masih lambat, kita harus masuk ke wilayah yang lebih teknis.

5. Bundle Size yang Tidak Terkontrol

Next.js menyediakan alat untuk menganalisis bundle yang dihasilkan, terutama untuk client bundle. Meskipun SC mengurangi client bundle, penggunaan CC yang sembarangan bisa menaikkan ukurannya.

Cara Mengecek: Gunakan @next/bundle-analyzer. Setelah menjalankan next build, alat ini akan menghasilkan visualisasi interaktif dari bundle kamu. Cari tahu:

Paket Gajah: Adakah library besar (misalnya Moment.js, atau utility library yang tidak di-tree-shake) yang masuk ke client bundle* padahal jarang dipakai?

  • Duplikasi: Apakah ada dependensi yang terduplikasi karena versi yang berbeda?

Dengan menganalisis bundle, kamu bisa memutus impor yang tidak perlu atau mengganti library berat dengan alternatif yang lebih ringan atau server-safe.

6. Server Actions dan Kesalahan Komunikasi

Server Actions adalah fitur keren yang memungkinkan mutasi data di server tanpa API layer terpisah. Karena mereka berjalan di server, mereka terintegrasi erat dengan Server Components.

Namun, build bisa melambat jika:

Payload Besar: Jika Server Action secara tidak sengaja mengembalikan payload data yang terlalu besar ke client (meskipun ini lebih ke runtime performance, ia bisa membebani build saat Next.js menghitung potensi payload dan optimization* rute). Error Handling Buruk: Failure yang tidak tertangani pada build time (misalnya pre-render yang gagal karena action tidak bisa terhubung ke DB) akan memaksa build untuk retry atau fail sepenuhnya setelah timeout* yang lama.

Pastikan Server Actions kamu ditulis seefisien mungkin dan hanya mengembalikan status ({ success: true } atau objek data minimal) kepada client.

---

Kapan Harus Panggil Tim Profesional? (Javapixa Creative Studio)

Memahami App Router, Server Components, dan optimization tingkat lanjut memerlukan waktu dan effort yang besar. Jika kamu adalah tim internal yang sedang berjuang melawan build time yang mendekati 10-20 menit, ini bukan hanya masalah code—ini masalah cost dan business agility.

Di sinilah peran Javapixa Creative Studio menjadi krusial.

Kami spesialis dalam modern stack Next.js, terutama arsitektur RSC dan App Router. Kami tidak hanya sekadar "memperbaiki bug." Tim kami akan melakukan audit mendalam terhadap arsitektur Server Component kamu:

  1. Server Component Boundary Audit: Kami akan memetakan seluruh component tree kamu untuk memastikan tidak ada "over-clienting" dan semua boundary CC diletakkan se-minimalis mungkin.
  2. Data Fetching Strategy Refinement: Kami akan mengeliminasi water-falling di seluruh aplikasi kamu, memastikan semua data fetching berjalan paralel dan menggunakan strategi caching yang optimal (baik untuk build time maupun runtime).
  3. Bundle Analysis dan Tree Shaking Maksimal: Menggunakan alat built-in dan custom analysis, kami memangkas client bundle hingga ke ukuran terkecil, memastikan build process berjalan cepat dan user experience (UX) di client tetap superior.

Meningkatkan performa build dari 15 menit menjadi 3 menit bukan hanya mimpi, itu adalah standar yang kami terapkan di setiap project Next.js 14/15 yang kami tangani. Kami membantu bisnis bertransformasi dengan memaksimalkan potensi penuh dari React Server Components.

---

Penutup: Fokus pada Efisiensi, Bukan Kecepatan Mentah

Next.js sudah dirancang untuk sangat cepat. Kalau build kamu lambat, itu adalah sinyal bahwa ada bottleneck arsitektural yang perlu diatasi. Jangan panik. Intinya adalah konsisten dalam menerapkan tiga prinsip dasar:

  1. Minimalisir use client: Letakkan use client serendah mungkin di component tree.
  2. Paralelkan Data Fetching: Gunakan Promise.all() di Server Component untuk menghindari water-falling.
  3. Definisikan Caching: Beri tahu Next.js kapan harus me-cache data pada build time dan kapan harus mengambil data live.

Dengan disiplin dalam implementasi Server Component, kamu tidak hanya akan mendapatkan build time yang lebih cepat, tapi juga aplikasi Next.js yang lebih efisien, lebih murah untuk di-host, dan jauh lebih menyenangkan untuk dikembangkan. Kalau kamu merasa perlu bantuan ahli untuk menyetel arsitektur Server Component kamu ke tingkat maksimal, tim Javapixa Creative Studio siap membantu mewujudkannya.