Next.js App Router memperkenalkan dua jenis komponen yang berbeda cara kerjanya: Server Components dan Client Components. Memahami perbedaannya adalah kunci untuk membangun aplikasi Next.js yang cepat dan efisien.
Server Components adalah komponen yang dirender sepenuhnya di server. HTML-nya sudah jadi sebelum dikirim ke browser — browser tidak perlu menjalankan JavaScript untuk menampilkannya.
Di Next.js App Router, semua komponen adalah Server Component secara default.
// app/page.tsx
// Ini Server Component — tidak perlu deklarasi apapun
async function BlogPage() {
const posts = await fetch('https://api.example.com/posts').then(r => r.json())
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
export default BlogPage
Perhatikan: kita bisa langsung async/await di dalam komponen, tanpa useEffect atau state management.
Client Components adalah komponen yang dirender di browser (seperti React biasa). Diperlukan saat komponen butuh interaktivitas — event handler, state, atau browser API.
Untuk mengubah komponen menjadi Client Component, tambahkan 'use client' di baris paling atas file.
'use client'
import { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
return (
<button onClick={() => setCount(count + 1)}>
Klik: {count}
</button>
)
}
export default Counter
| Server Component | Client Component | |
|---|---|---|
| Render | Di server | Di browser |
| Default? | ✅ Ya | ❌ Perlu 'use client' |
Bisa async/await? |
✅ Ya | ❌ Tidak langsung |
Bisa useState/useEffect? |
❌ Tidak | ✅ Ya |
| Bisa akses database langsung? | ✅ Ya | ❌ Tidak |
| Bisa pakai event handler? | ❌ Tidak | ✅ Ya |
| Dikirim ke browser sebagai JS? | ❌ Tidak | ✅ Ya |
Kuncinya adalah mendorong Client Components sejauh mungkin ke "daun" (leaf) dari component tree.
// ✅ Pola yang benar
// app/product/page.tsx — Server Component
async function ProductPage({ params }) {
const product = await getProduct(params.id) // fetch langsung di server
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<AddToCartButton productId={product.id} /> {/* Client Component */}
</div>
)
}
// app/product/AddToCartButton.tsx — Client Component
'use client'
function AddToCartButton({ productId }) {
function handleClick() {
// logika tambah ke cart
}
return <button onClick={handleClick}>Tambah ke Keranjang</button>
}
Dengan pola ini, hanya AddToCartButton yang dikirim sebagai JavaScript ke browser — bukan seluruh halaman.
// ❌ Hindari ini
'use client'
async function ProductPage() { // Seluruh halaman jadi Client Component
const product = await getProduct() // Ini tidak bisa di Client Component!
// ...
}
Ini konsep penting: Server Component bisa menjadi parent dari Client Component, tapi tidak sebaliknya — Client Component tidak bisa mengimport Server Component.
// ✅ Ini valid
// Server Component sebagai parent
function Layout({ children }) {
return <div>{children}</div>
}
// Di dalamnya ada Client Component
<Layout>
<InteractiveWidget /> {/* Client Component */}
</Layout>
// ❌ Ini tidak valid
'use client'
import ServerComponent from './ServerComponent' // Error!
function ClientComponent() {
return <ServerComponent /> // Tidak bisa
}
// app/blog/page.tsx — Server Component
async function BlogPage() {
const posts = await db.post.findMany() // Akses DB langsung
return (
<main>
<h1>Blog</h1>
<SearchBar /> {/* Client Component untuk fitur search */}
<PostList posts={posts} /> {/* Server Component */}
</main>
)
}
// components/SearchBar.tsx — Client Component
'use client'
import { useState } from 'react'
function SearchBar() {
const [query, setQuery] = useState('')
return (
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Cari artikel..."
/>
)
}
// components/PostList.tsx — Server Component (tidak perlu 'use client')
function PostList({ posts }) {
return (
<ul>
{posts.map(post => (
<li key={post.id}>
<a href={`/blog/${post.slug}`}>{post.title}</a>
</li>
))}
</ul>
)
}
Aturan sederhananya:
onClickDengan memahami pola ini, aplikasi Next.js kamu akan lebih cepat karena JavaScript yang dikirim ke browser jadi jauh lebih sedikit.
Tidak. Server Component adalah default, tapi kamu bebas mengubahnya jadi Client Component dengan menambahkan 'use client' di bagian atas file. Gunakan Client Component hanya saat benar-benar dibutuhkan — misalnya saat ada interaktivitas atau penggunaan browser API.
Bisa. Bahkan ini adalah pola yang dianjurkan. Server Component bisa menjadi parent dari Client Component. Yang tidak bisa adalah sebaliknya — Client Component tidak bisa mengimport Server Component.
Tetap dirender di server untuk menghasilkan HTML awal (ini disebut SSR), tapi JavaScript-nya juga dikirim ke browser untuk proses hydration agar interaktivitas bisa berjalan. Berbeda dengan Server Component yang JavaScript-nya tidak dikirim ke browser sama sekali.
Karena kode Server Component tidak dimasukkan ke dalam JavaScript bundle yang dikirim ke browser. Hanya Client Component yang masuk ke bundle. Semakin sedikit Client Component yang dibuat, semakin kecil bundle-nya.
Tidak. Server Components hanya tersedia di App Router (Next.js 13+). Jika masih menggunakan Pages Router, semua komponen berperilaku seperti Client Component.
Gunakan fungsi bawaan Next.js seperti cookies() atau headers() dari next/headers. Fungsi ini hanya bisa dipanggil di Server Component atau Server Action.