Server Component adalah komponen React yang jalan cuma di server: hasil render-nya dikirim ke browser, kodenya gak pernah. Komponen yang tetap jalan di browser, untuk bagian yang interaktif, disebut Client Component. React 19 menyediakan keduanya, tapi kamu memakainya lewat framework yang mengurus server-nya, paling sering Next.js dengan App Router. Bayangin restoran. Dapur (server) memasak, dan yang sampai ke mejamu adalah makanan jadi, bukan resep dan kompornya. Cuma yang memang dikerjakan di meja, misalnya sambal yang kamu tuang sendiri, dibawakan alatnya. Dapur juga boleh membuka gudang bahan, misalnya database dan kunci rahasia, yang gak pernah dilihat tamu.
🧪 Tebakan cepat: halaman produk berisi nama, harga, deskripsi yang dirender dengan library markdown 36 KB, dan satu tombol +1. Kalau cuma tombolnya yang jadi Client Component, kode komponen apa yang dikirim ke browser? Tebak dulu, jawabannya ada di bagian "Apa yang dikirim ke browser".
Server Component: default, dan boleh async
// app/produk/page.tsx: Server Component (gak ada "use client")
import { db } from "@/lib/db" // contoh library database
import Deskripsi from "./Deskripsi"
import Counter from "./Counter"
export default async function ProdukPage() {
const produk = await db.produk.findFirst() // langsung ke database
return (
<main>
<h1>{produk.nama}</h1>
<p>Rp{produk.harga}</p>
<Deskripsi teks={produk.deskripsi} />
<Counter />
</main>
)
}
- Di Next.js App Router, setiap komponen di folder
appadalah Server Component, kecuali file-nya ditandai"use client". async functiondanawait: Server Component boleh menunggu data sebelum render. Gak perluuseEffectatau loading state untuk data ini.db.produk.findFirst()jalan di server, jadi boleh memakai koneksi database dan secret dari environment variable, misalnyaprocess.env.DATABASE_URL, tanpa bocor ke browser.- Yang gak bisa:
useState,useEffect, event handler sepertionClick, dan API browser sepertiwindowataulocalStorage. Di server gak ada browser, dan kode komponen ini gak pernah sampai ke browser.
Client Component: "use client" di baris pertama
// app/produk/Counter.tsx: Client Component
"use client"
import { useState } from "react"
export default function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p id="count">{count}</p>
<button id="increment" onClick={() => setCount(count + 1)}>+1</button>
</div>
)
}
"use client"ditulis paling atas, sebelum import. Itu batasnya (boundary): file ini dan semua yang ia import ikut dikirim ke browser.- Di sini
useStatedanonClickboleh dipakai. - Client Component tetap dirender jadi HTML di server untuk tampilan pertama. Di browser, React lalu "menghidupkan" HTML itu dengan memasang event handler-nya; namanya hydration. Jadi "client" artinya kodenya ikut dikirim dan jalan di browser.
Apa yang dikirim ke browser
Dengan pembagian tadi, browser menerima tiga hal:
- HTML halaman yang sudah jadi: nama, harga, deskripsi, tombol, angka 0.
- RSC payload: hasil render Server Component dalam format React, ditambah catatan "di sini ada Counter, ini props-nya".
- JavaScript: runtime React dan Next.js, lalu kode Counter saja.
Itu jawaban tebakannya: dari semua komponen di halaman itu, cuma kode Counter yang dikirim. ProdukPage, Deskripsi, dan library markdown-nya tinggal di server. Ini angka contoh yang sama dengan di Lab (ukuran sesudah dikompres gzip):
"use client" ditaruh di |
JavaScript yang dikirim |
|---|---|
Counter saja |
runtime 100 KB + Counter 1 KB = 101 KB |
Deskripsi dan Counter |
100 + 1 + Deskripsi 1 + library markdown 36 = 138 KB |
ProdukPage (paling atas) |
gagal: komponen async dan akses database gak bisa jalan di browser |
Deskripsi gak interaktif, jadi menjadikannya Client Component cuma menambah 37 KB yang harus diunduh dan dijalankan tiap pengunjung, tanpa ada yang bisa diklik.
Menggabungkan: server di luar, client sekecil mungkin
- Server Component boleh merender Client Component dan mengirim props. Props itu harus bisa diserialisasi, yaitu diubah jadi data untuk dikirim: teks, angka, boolean, array, object biasa, juga
Date,Map, danSet. Fungsi biasa gak bisa. - Client Component gak bisa meng-import Server Component: apa pun yang di-import file
"use client"ikut jadi client. Kalau butuh, kirim Server Component sebagaichildren. - Taruh
"use client"serendah mungkin di pohon komponen, cuma di bagian yang butuh interaksi.
⚠️ Jebakan yang sering ketemu
useStateatauonClickdi Server Component. Next.js menolaknya saat build dan menyarankan menambahkan"use client". Pindahkan bagian interaktifnya ke komponen kecil sendiri yang diberi"use client"."use client"di paling atas pohon. Semua anaknya ikut jadi client, JavaScript-nya membengkak, dan komponenasyncyang mengambil data langsung gak bisa dipakai lagi.- Secret di Client Component. Di Next.js, environment variable yang namanya gak diawali
NEXT_PUBLIC_gak tersedia di browser, isinyaundefined. Ambil data rahasia di Server Component. - Latihan. Editor latihan menjalankan React biasa di browser, tanpa Next.js, jadi semua komponennya client. Tulis Counter seperti biasa dengan
useState;"use client"boleh ditulis, boleh juga gak.
TL;DR: Server Component jalan cuma di server: boleh async/await, boleh langsung ke database dan memakai secret, tapi gak boleh state, effect, event handler, atau API browser. Hasil render-nya yang dikirim, kodenya gak. Client Component ditandai "use client" di baris pertama file; kodenya ikut dikirim ke browser supaya useState dan onClick jalan. Di Next.js App Router, komponen adalah Server Component secara default. Taruh "use client" serendah mungkin, kirim props yang bisa diserialisasi, dan JavaScript yang diunduh pengunjung tetap kecil: di contoh ini 101 KB, bukan 138 KB.