Nextjs Routing Server Client Data Fetching
Definisi
Di Next.js App Router, routing, rendering, dan pengambilan data saling terhubung lewat struktur folder. Satu file page tidak hanya mewakili halaman, tapi juga bagian dari alur render yang bisa dijalankan di server, di browser, atau kombinasi keduanya.
Topik inti di halaman ini:
- App Router: sistem routing berbasis folder di direktori
app/ - Server Component: komponen default yang dirender di server
- Client Component: komponen dengan interaksi browser seperti state dan event handler
- Data fetching: cara mengambil data di server, client, atau lewat route handler
Kenapa penting
Kalau pola ini tidak dipahami, proyek Next.js cepat jadi campur aduk:
- semua komponen dipindah ke client padahal tidak perlu
- data diambil di tempat yang salah
- halaman terasa lambat karena bundle membesar
- interaksi UI malah rusak karena server component dipaksa menangani state browser
Di Next.js, keputusan soal server/client bukan detail kecil. Keputusan itu memengaruhi:
- kapan HTML bisa tampil
- apa yang masuk ke JavaScript bundle
- bagaimana data dirender ulang
- apakah UI bisa interaktif di browser
- seberapa mudah halaman di-cache atau di-stream
Cara kerja App Router
App Router memakai struktur folder untuk membentuk route. Contoh sederhana:
app/
layout.tsx
page.tsx
dashboard/
page.tsx
products/
[id]/
page.tsxSetiap folder punya peran:
page.tsx= isi routelayout.tsx= kerangka yang membungkus route turunanloading.tsx= UI saat route sedang diproseserror.tsx= fallback saat render gagalnot-found.tsx= tampilan 404 pada segment tertentu
Cara bacanya sederhana:
app/page.tsx→ route/app/dashboard/page.tsx→ route/dashboardapp/products/[id]/page.tsx→ route dinamis seperti/products/123
Layout bersifat bertingkat. Root layout membungkus semua route, lalu sub-layout membungkus segment di bawahnya. Karena itu App Router cocok untuk UI yang punya area tetap, misalnya navbar, sidebar, atau shell dashboard.
Server Component dan Client Component
Secara default, file di App Router adalah Server Component. Kalau ingin komponen berjalan di browser, file itu harus diberi directive:
'use client'Server Component
Server Component dipakai untuk bagian yang tidak butuh interaksi browser langsung.
Ciri umum:
- dirender di server
- bisa akses data source langsung dari server
- tidak membawa hook browser seperti
useStatedanuseEffect - bundle ke client lebih kecil karena logika tetap di server
Cocok untuk:
- halaman yang dominan membaca data
- layout dan shell aplikasi
- konten statis atau semi-statis
- komponen presentasional yang tidak butuh event handler browser
Client Component
Client Component dipakai ketika UI butuh interaksi browser.
Ciri umum:
- dirender ulang di browser setelah hidrasi
- bisa memakai
useState,useEffect,useRef, event handler, dan hook browser lain - ikut masuk ke JavaScript bundle client
Cocok untuk:
- form interaktif
- dropdown, modal, tab, carousel
- pencarian live
- state UI lokal
- komponen yang bergantung pada API browser atau event DOM
Aturan praktis
Pakai server component sebagai default. Naikkan ke client component hanya untuk bagian yang benar-benar butuh interaksi.
Itu berarti pola yang sehat biasanya begini:
- halaman dan data utama tetap di server
- komponen kecil yang interaktif dipisah jadi client component
- server component boleh membungkus client component
- client component tidak perlu menelan seluruh halaman
Kalau terlalu banyak area dijadikan client component, efeknya biasanya langsung terasa di bundle size dan waktu hidrasi.
Data fetching
Di App Router, data bisa diambil dengan beberapa pola. Yang paling penting bukan hanya “bisa ambil dari mana”, tapi “sebaiknya di mana”.
1. Fetch di Server Component
Ini pola yang paling umum untuk data halaman.
async function getPosts() {
const res = await fetch('https://example.com/api/posts')
return res.json()
}
export default async function Page() {
const posts = await getPosts()
return (
<ul>
{posts.map((post: { id: string; title: string }) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}Kelebihan pola ini:
- data sudah tersedia saat render server
- HTML awal bisa berisi konten nyata
- cocok untuk SEO dan first paint
- logika fetch tetap dekat dengan halaman yang memakai data
2. Fetch di Client Component
Pakai ini kalau data bergantung pada interaksi browser atau update setelah halaman tampil.
'use client'
import { useEffect, useState } from 'react'
export default function Notifications() {
const [items, setItems] = useState<string[]>([])
useEffect(() => {
fetch('/api/notifications')
.then((res) => res.json())
.then(setItems)
}, [])
return <pre>{JSON.stringify(items, null, 2)}</pre>
}Pola ini cocok untuk:
- data yang berubah terus setelah render awal
- polling atau refresh berkala
- data yang bergantung pada state browser
- UI yang harus bereaksi setelah event user
Biayanya adalah data tidak langsung ada pada HTML awal, jadi UI sering perlu loading state.
3. Fetch lewat Route Handler
Route handler dipakai kalau ingin membuat endpoint internal di dalam aplikasi Next.js.
Contoh lokasi:
app/api/posts/route.tsPola ini berguna untuk:
- menyembunyikan akses ke sumber data tertentu
- merapikan logika integrasi
- memberi endpoint untuk client component
- membuat API internal yang konsisten
4. Server Actions
Server Actions berguna untuk aksi yang mengubah data dari form atau interaksi UI, lalu diproses di server.
Meskipun bukan pengganti data fetching murni, server actions sering dipasangkan dengan fetch karena alurnya saling terkait: baca data di server, kirim perubahan ke server, lalu refresh atau revalidate hasilnya.
Alur request dan render
Secara praktis, urutannya biasanya seperti ini:
- User membuka route.
- Next.js mencocokkan URL dengan folder di
app/. - Layout yang relevan dimuat.
- Server Component dirender di server.
- Data diambil selama render server jika dibutuhkan.
- HTML awal dikirim ke browser.
- Client Component di-hydrate agar interaktif.
- Interaksi user berjalan di browser.
Diagram ini menunjukkan batas penting: server menyelesaikan render awal, lalu browser mengambil alih bagian yang interaktif.
Pembagian tanggung jawab server dan client
Cara paling aman membaginya adalah seperti ini:
Pembagian ini penting karena setiap sisi punya tugas berbeda:
- Server: routing, data prep, render awal, integrasi backend
- Client: interaksi, state lokal, event browser, update UI setelah halaman tampil
Implikasi untuk rendering
Pemilihan server atau client component punya efek langsung ke rendering.
Kalau memakai server component
- HTML awal lebih kaya isi
- loading awal biasanya lebih cepat untuk halaman berbasis data
- JavaScript yang dikirim ke browser cenderung lebih sedikit
- rendering ulang untuk data bisa lebih terkontrol
Kalau memakai client component
- browser perlu menunggu hidrasi untuk interaksi penuh
- bundle client membesar kalau terlalu banyak komponen dipindah ke browser
- lebih cocok untuk UI yang hidup dan banyak event
- data sering perlu loading state karena tidak muncul pada render server awal
Trade-off utama
- Server component unggul untuk konten dan data awal
- Client component unggul untuk interaksi dan state yang berubah di browser
- Campuran keduanya biasanya hasil paling sehat
Contoh pola komposisi yang sehat
Satu halaman biasanya tidak sepenuhnya server atau client. Yang umum adalah komposisi seperti ini:
// app/dashboard/page.tsx
import DashboardFilters from './dashboard-filters'
export default async function Page() {
const data = await getDashboardData()
return (
<section>
<h1>Dashboard</h1>
<DashboardFilters />
<DashboardSummary data={data} />
</section>
)
}Lalu DashboardFilters bisa menjadi client component kalau butuh state dropdown atau input.
Pola ini menjaga:
- data utama tetap dirender di server
- interaksi hanya hidup di komponen yang butuh
- bundle client tetap terkendali
Catatan implementasi
- Jadikan server component sebagai default.
- Naikkan ke client component hanya kalau butuh state, efek, atau event browser.
- Taruh data fetching sedekat mungkin dengan pemakaian datanya.
- Hindari memindahkan satu halaman penuh ke client hanya karena ada satu tombol interaktif.
- Gunakan route handler saat butuh endpoint internal yang dipanggil client.
- Gunakan server action untuk aksi perubahan data dari form atau interaksi server-side.
- Kalau halaman terasa lambat, cek dulu apakah terlalu banyak komponen di client.
- Kalau SEO dan konten awal penting, utamakan fetch di server.
- Kalau data bergantung pada event user atau API browser, fetch di client lebih masuk akal.
Kesalahan umum
- Menambahkan
'use client'di root komponen tanpa alasan jelas. - Mengambil semua data di
useEffectpadahal data itu dibutuhkan saat render awal. - Memakai client component untuk komponen yang sebenarnya statis.
- Menaruh interaksi kompleks di server component, lalu bingung kenapa hook browser tidak bisa dipakai.
- Mencampur server-only logic dan browser logic dalam satu file tanpa batas yang jelas.
Istilah
- App Router: sistem routing Next.js berbasis folder di
app/. - Layout: wrapper yang dipakai bersama oleh beberapa route.
- Server Component: komponen yang dirender di server dan tidak membawa interaksi browser.
- Client Component: komponen yang berjalan di browser dan bisa memakai state serta event handler.
- Hydration: proses membuat UI yang sudah dirender server menjadi interaktif di browser.
- Route Handler: endpoint API di dalam aplikasi Next.js.
- Server Action: fungsi server yang dipanggil dari UI untuk aksi perubahan data.
- Revalidation: proses memperbarui cache atau render ulang data setelah perubahan.
Referensi cepat
- App Router dipakai untuk struktur route berbasis folder.
- Server component dipakai untuk render awal dan data fetching server-side.
- Client component dipakai untuk interaksi browser.
- Data fetching paling aman dimulai dari kebutuhan render dan interaksi, bukan dari kebiasaan lama semata.