Kembali ke Blog
Web Development#react#server components#nextjs#rsc#frontend#2026

React Server Components: Cara Berpikir yang Benar untuk Developer 2026

RSC bukan cuma fitur baru — ini perubahan paradigma cara kita tulis React. Setelah 2 tahun sejak adopsi luas, ini panduan komprehensif cara berpikir dan implementasi RSC yang benar.

Muhamad Putra Aulia Hidayat

Muhamad Putra Aulia Hidayat

22 Maret 20264 menit baca

React Server Components: Mental Model yang Benar

Setelah 2 tahun React Server Components (RSC) jadi mainstream lewat Next.js App Router, masih banyak developer yang salah kaprah. Artikel ini untuk meluruskan.

Pertanyaan Dasar: Server atau Client?

Aturan sederhana:

Default: Server Component
Gunakan Client Component HANYA kalau:
- Butuh useState atau useReducer
- Butuh useEffect
- Butuh browser APIs (window, document, localStorage)
- Butuh event handlers (onClick, onChange, dll)
- Butuh third-party library yang tidak support server

Visualisasi Component Tree

app/
└── Layout (Server)           ← Fetch user dari DB
    ├── Navbar (Server)        ← Render navigasi
    │   └── UserMenu (Client)  ← Butuh state untuk dropdown
    ├── Sidebar (Server)       ← Data statis
    └── Main (Server)
        ├── ProductList (Server)  ← Fetch dari DB langsung
        │   └── AddToCart (Client)  ← Butuh state cart
        └── Pagination (Client)   ← Butuh state halaman

Server Components bisa render Client Components, tapi Client Components tidak bisa render Server Components (secara langsung).

Fetch Data di Server Component

// app/products/page.tsx - Server Component
import { createClient } from "@/lib/supabase/server"

export default async function ProductsPage() {
  // Fetch langsung di component, tanpa useEffect atau SWR
  const supabase = await createClient()
  const { data: products } = await supabase
    .from("products")
    .select("*")
    .order("created_at", { ascending: false })
  
  return (
    <div>
      <h1>Products</h1>
      <ProductGrid products={products ?? []} />
    </div>
  )
}

// ProductGrid juga Server Component
function ProductGrid({ products }: { products: Product[] }) {
  return (
    <div className="grid grid-cols-3 gap-4">
      {products.map(p => (
        <ProductCard key={p.id} product={p} />
        // ProductCard bisa Server Component kalau tidak butuh interaktivitas
      ))}
    </div>
  )
}

Pass Server Data ke Client Component

// Server Component: fetch data, pass ke Client
export default async function CartPage() {
  const supabase = await createClient()
  const { data: items } = await supabase.from("cart_items").select("*, product:products(*)")
  
  // Pass data sebagai props ke Client Component
  return <CartClient initialItems={items ?? []} />
}

// Client Component: handle interactivity
"use client"
function CartClient({ initialItems }: { initialItems: CartItem[] }) {
  const [items, setItems] = useState(initialItems)
  
  const removeItem = (id: string) => {
    setItems(prev => prev.filter(i => i.id !== id))
    // Optimistic update + server action
  }
  
  return (
    <div>
      {items.map(item => (
        <div key={item.id}>
          {item.product.name}
          <button onClick={() => removeItem(item.id)}>Hapus</button>
        </div>
      ))}
    </div>
  )
}

Server Actions: Forms Tanpa API Routes

// app/actions.ts
"use server"
import { revalidatePath } from "next/cache"

export async function createProduct(formData: FormData) {
  const name = formData.get("name") as string
  const price = Number(formData.get("price"))
  
  // Validasi
  if (!name || price <= 0) {
    return { error: "Data tidak valid" }
  }
  
  const supabase = await createServerClient()
  const { error } = await supabase.from("products").insert({ name, price })
  
  if (error) return { error: "Gagal menyimpan produk" }
  
  // Revalidate halaman produk
  revalidatePath("/products")
  return { success: true }
}

// Pakai di form - tidak perlu API route!
function ProductForm() {
  return (
    <form action={createProduct}>
      <input name="name" placeholder="Nama produk" />
      <input name="price" type="number" placeholder="Harga" />
      <button type="submit">Simpan</button>
    </form>
  )
}

Kesalahan Umum yang Harus Dihindari

// SALAH: Fetch di Client Component
"use client"
function Products() {
  const [products, setProducts] = useState([])
  useEffect(() => {
    fetch("/api/products").then(r => r.json()).then(setProducts)
  }, [])
  // Loading state, error handling yang manual...
}

// BENAR: Fetch di Server Component
async function Products() {
  const products = await getProducts() // Langsung, clean
  return <div>{products.map(...)}</div>
}

// SALAH: Client Component yang bisa jadi Server
"use client"
function ProductCard({ product }: { product: Product }) {
  // Tidak ada state/event, tidak perlu "use client"!
  return <div>{product.name}</div>
}

Kuncinya: selalu tanya "apakah komponen ini butuh interaktivitas atau browser API?" Kalau tidak, biarkan jadi Server Component.

reactserver componentsnextjsrscfrontend2026

Newsletter Digital Uptime

Tips teknologi & bisnis mingguan

Bergabung dengan 2,500+ subscriber yang mendapatkan insight teknologi, tutorial development, dan tips bisnis digital langsung ke inbox mereka setiap minggu.

Tidak ada spam. Unsubscribe kapan saja.

Artikel Terkait

Kami menggunakan cookies untuk meningkatkan pengalaman Anda di website ini. Dengan melanjutkan, Anda menyetujui penggunaan cookies sesuai Kebijakan Privasi kami.