Kenapa Ini Penting?
Kalau kamu baru mulai belajar Next.js 15, pasti bingung: "Kok ada Server Component, Client Component, terus ada lagi Server Actions? Kapan pakai yang mana?"
Ini masalah yang sering dialami developer pemula. Kalau salah pilih, aplikasi bisa jadi lambat (karena terlalu banyak JavaScript di browser), atau malah error (karena coba akses database dari client).
Di artikel ini, kita akan belajar cara memilih yang tepat dengan contoh praktis yang bisa langsung kamu coba.
Apa yang Akan Kamu Pelajari
- Perbedaan Server Component dan Client Component
- Apa itu Server Actions dan kapan menggunakannya
- Pattern hybrid: menggabungkan server dan client
- Contoh kode lengkap yang bisa langsung dicoba
- Kesalahan umum dan cara menghindarinya
Prasyarat
Sebelum mulai, pastikan kamu sudah:
- Paham dasar React (component, props, state)
- Pernah install Next.js (minimal versi 14 atau 15)
- Tahu cara menjalankan npm run dev
Konsep Dasar: Server vs Client
Bayangkan kamu punya restoran. Ada dua tempat kerja:
- Dapur (Server): tempat masak, akses bahan mentah, resep rahasia. Pelanggan tidak boleh masuk.
- Meja makan (Client/Browser): tempat pelanggan duduk, pesan menu, lihat makanan.
Di Next.js 15:
- Server Component = dapur. Bisa akses database, environment variables, API keys.
- Client Component = meja makan. Bisa interaksi dengan user (klik tombol, isi form).
- Server Actions = pelayan. Bawa pesanan dari meja ke dapur, bawa makanan dari dapur ke meja.
Server Component: Kapan Pakai?
Server Component adalah default di Next.js 15 App Router. Semua file di folder app/ otomatis jadi Server Component kecuali kamu tulis 'use client' di atas.
Gunakan Server Component untuk:
- Fetch data dari database atau API eksternal
- Akses environment variables (API keys, secrets)
- Render konten statis yang tidak butuh interaksi
Contoh: Halaman blog yang menampilkan daftar artikel.
// app/posts/page.tsx (Server Component - default)
import { db } from '@/lib/db';
export default async function PostsPage() {
// Fetch langsung dari database - aman karena di server
const posts = await db.post.findMany();
return (
<div>
<h1>Daftar Artikel</h1>
{posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
))}
</div>
);
}Keuntungan: Tidak ada JavaScript dikirim ke browser untuk bagian ini. Halaman lebih cepat.
Client Component: Kapan Pakai?
Client Component digunakan ketika kamu butuh interaksi dengan user atau akses browser API.
Untuk membuat Client Component, tambahkan 'use client' di baris pertama file.
Gunakan Client Component untuk:
- State management (useState, useReducer)
- Event handlers (onClick, onChange, onSubmit)
- Browser APIs (localStorage, window, navigator)
- React hooks (useEffect, useRef, custom hooks)
- Library yang butuh React hooks (chart, date picker)
Contoh: Form pencarian artikel.
// app/posts/search-form.tsx (Client Component)
'use client';
import { useState } from 'react';
export function SearchForm({ onSearch }: Props) {
const [query, setQuery] = useState('');
const [loading, setLoading] = useState(false);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setLoading(true);
await onSearch(query);
setLoading(false);
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Cari artikel..."
/>
<button type="submit" disabled={loading}>
{loading ? 'Mencari...' : 'Cari'}
</button>
</form>
);
}Perhatikan: Ada 'use client' di atas, dan kita pakai useState untuk menyimpan input user.
Server Actions: Jembatan Server-Client
Server Actions adalah fungsi yang berjalan di server, tapi bisa dipanggil dari Client Component.
Ini seperti pelayan di restoran: Client Component (meja) kirim pesanan, Server Action (pelayan) bawa ke dapur (server), lalu bawa hasilnya kembali.
Gunakan Server Actions untuk:
- Form submission (create, update, delete data)
- Operasi yang butuh akses database/API dari client
- Validasi data di server sebelum disimpan
Contoh: Fungsi untuk mencari artikel.
// app/posts/actions.ts (Server Action)
'use server';
import { db } from '@/lib/db';
export async function searchPosts(query: string) {
// Validasi input
if (!query || query.length < 2) {
return { error: 'Query minimal 2 karakter' };
}
// Query database - aman karena di server
const posts = await db.post.findMany({
where: {
OR: [
{ title: { contains: query } },
{ content: { contains: query } }
]
},
take: 10
});
return { posts };
}Perhatikan: Ada 'use server' di atas. Ini menandakan fungsi berjalan di server, bukan di browser.
Pattern Hybrid: Menggabungkan Semuanya
Sekarang kita gabungkan ketiga konsep tadi: Server Component untuk fetch data awal, Client Component untuk interaksi, Server Action untuk operasi server.
Contoh lengkap: Halaman artikel dengan fitur pencarian.
Step 1: Server Component (Halaman Utama)
// app/posts/page.tsx (Server Component)
import { db } from '@/lib/db';
import { PostList } from './post-list';
export default async function PostsPage() {
// Fetch data awal di server
const initialPosts = await db.post.findMany({
take: 20,
orderBy: { createdAt: 'desc' }
});
return (
<div>
<h1>Artikel Terbaru</h1>
{/* Pass data ke Client Component */}
<PostList initialPosts={initialPosts} />
</div>
);
}Step 2: Client Component (Interaksi)
// app/posts/post-list.tsx (Client Component)
'use client';
import { useState } from 'react';
import { searchPosts } from './actions';
interface Props {
initialPosts: Post[];
}
export function PostList({ initialPosts }: Props) {
const [posts, setPosts] = useState(initialPosts);
const [query, setQuery] = useState('');
const [loading, setLoading] = useState(false);
async function handleSearch(e: React.FormEvent) {
e.preventDefault();
if (!query) {
setPosts(initialPosts);
return;
}
setLoading(true);
const result = await searchPosts(query);
if (result.posts) {
setPosts(result.posts);
}
setLoading(false);
}
return (
<div>
<form onSubmit={handleSearch}>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Cari artikel..."
/>
<button type="submit" disabled={loading}>
{loading ? 'Mencari...' : 'Cari'}
</button>
</form>
<div>
{posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
))}
</div>
</div>
);
}Step 3: Server Action (Operasi Server)
File actions.ts sudah kita buat di bagian sebelumnya. Ini yang dipanggil dari Client Component.
Kesalahan Umum yang Harus Dihindari
1. Pakai 'use client' di Semua File
Kesalahan: Developer pemula sering tambah 'use client' di semua file karena takut error.
Dampak: Bundle JavaScript jadi besar, halaman lambat.
Solusi: Default ke Server Component. Tambah 'use client' hanya di component yang butuh interaksi.
2. Akses Database dari Client Component
Kesalahan: Coba import database client di file dengan 'use client'.
// ❌ SALAH
'use client';
import { db } from '@/lib/db'; // Error!
export function MyComponent() {
const posts = await db.post.findMany(); // Tidak bisa!
}Dampak: Error saat build atau runtime.
Solusi: Fetch data di Server Component atau pakai Server Action.
3. Lupa 'use server' di Server Actions
Kesalahan: Buat fungsi async tapi lupa tambah 'use server'.
Dampak: Fungsi tidak bisa dipanggil dari Client Component.
Solusi: Selalu tambah 'use server' di file actions.
4. Tidak Handle Loading State
Kesalahan: Panggil Server Action tanpa tampilkan loading indicator.
Dampak: User bingung, klik tombol berkali-kali.
Solusi: Pakai useState untuk track loading, disable button saat loading.
Kesimpulan
Sekarang kamu sudah paham perbedaan Server Component, Client Component, dan Server Actions di Next.js 15:
- Server Component: untuk fetch data, akses database, render statis
- Client Component: untuk interaksi user, state, event handlers
- Server Actions: jembatan antara client dan server untuk operasi data
Pattern terbaik: Default ke Server Component, tambah Client Component hanya di bagian yang butuh interaksi, pakai Server Actions untuk operasi data dari client.
Langkah Selanjutnya
- Coba buat project Next.js 15 baru dengan npx create-next-app@latest
- Implementasi contoh di artikel ini
- Pelajari React Server Components lebih dalam di dokumentasi Next.js
- Eksperimen dengan form handling menggunakan Server Actions
Selamat belajar! 🚀
🚀 Takeaways
Server Component: Default di Next.js 15 App Router, buat fetch data, akses database, render static content. No JS shipped ke browser → faster load.
Client Component: Tambah 'use client' di atas file, buat interaktivitas (state, event handlers, browser APIs). JS dikirim ke browser.Server Actions: Tambah 'use server', jembatan antara client dan server buat form submission, DB operations dari client. Aman karena dijalankan di server.
Best pattern: Default ke Server Component, tambah Client Component cuma di bagian yang butuh interaksi, pakai Server Actions buat data operations. Keep bundle small, keep app fast.