React Server Components vs Arsitektur Islands

Foto oleh Capt. Anastasia Schmidt via Wikimedia Commons (Public domain)
React Server Components menjadikan setiap komponen default ke server dan membutuhkan directive use client eksplisit untuk mengirim JavaScript interaktif, sehingga hydration hanya terjadi di batas tersebut. Arsitektur islands, dipakai Astro dan Qwik, menjadikan seluruh halaman default sebagai HTML statis dan hanya melakukan hydration pada komponen yang Anda tandai sebagai island. RSC meminta Anda membenarkan kode klien; islands meminta Anda membenarkan interaktivitas.
Tidak. Server components sendiri tidak mengirim JavaScript dan memiliki biaya hydration nol, tetapi komponen apa pun yang ditandai use client tetap di-hydrate di browser seperti React biasa. RSC mengurangi hydration hanya ke batas interaktif pohon Anda, bukan menghapusnya sepenuhnya. Resumability Qwik adalah model yang benar-benar menghindari replay hydration seluruhnya.
Partial hydration, seperti di Astro, tetap melakukan hydration pada setiap island interaktif dengan mengeksekusi ulang kode komponennya di browser. Resumability Qwik justru menserialisasi state aplikasi dan lokasi event handler ke dalam HTML lalu melanjutkan di klien tanpa menjalankan ulang kode itu. JavaScript untuk sebuah handler diambil hanya saat pengguna benar-benar berinteraksi, menjaga eksekusi awal mendekati nol.
Pilih Astro ketika situs Anda mengutamakan konten — halaman marketing, dokumentasi, blog, atau landing page di mana sebagian besar konten statis dan interaktivitas adalah pengecualian. Astro mengirim nol JavaScript secara default dan memungkinkan Anda menambahkan island kecil yang terisolasi. Next.js dengan RSC lebih cocok untuk produk berbentuk aplikasi dengan interaktivitas padat dan state klien bersama.
Biasanya ya, tetapi hanya jika Anda menjaga batas use client dekat daun pohon komponen. Jika Anda menandai layout tingkat atas sebagai client component, setiap anak ikut tertarik ke bundle klien dan penghematannya hilang. Dorong directive sejauh mungkin ke bawah menuju elemen interaktif yang sebenarnya untuk memaksimalkan pengurangan.

Foto oleh Capt. Anastasia Schmidt via Wikimedia Commons (Public domain)
Ringkasan Utama
React Server Components dirender di server dan tidak mengirim JavaScript kecuali komponen ditandai interaktif, sehingga biaya hydration turun hanya pada batas interaktif. Arsitektur islands di Astro dan Qwik membalik default: halaman adalah HTML statis, dan Anda memilih komponen tertentu untuk hydration atau resumability. Keduanya memangkas JavaScript; bedanya di mana batas itu berada.
Setiap framework frontend modern kini mengejar tujuan yang sama: mengirim lebih sedikit JavaScript ke browser. Era single-page-app mengajarkan kita biaya mengirim runtime React penuh plus setiap komponen ke klien, lalu menjalankannya kembali saat hydration hanya untuk memasang event listener ke HTML yang sudah diproduksi server. Dua kelompok jawaban muncul. React Server Components (RSC), kini stabil di React 19 dan default di Next.js 15, serta arsitektur islands yang dipopulerkan Astro dan didorong lebih jauh oleh model resumability Qwik.
Saya sudah mengirim keduanya. Pada sebuah layanan Next.js yang saya jalankan, memindahkan halaman padat data ke server components memangkas bundle klien secara nyata karena fetching, formatting, dan markup tidak pernah menyeberang ke browser. Pada sebuah situs marketing yang sebagian besar statis dan saya bangun ulang di Astro, insting yang sama diterapkan secara berbeda: halaman dimulai sebagai HTML dan saya hanya membayar JavaScript di tempat yang eksplisit saya minta. Memahami mengapa dua pengalaman itu terasa berbeda dimulai dari model hydration.
Server-side rendering klasik mengirim HTML yang sudah jadi, bagus untuk first paint. Tetapi browser kemudian mengunduh bundle JavaScript, membangun ulang pohon komponen di memori, menelusuri DOM yang ada, dan memasang listener. Replay itu adalah hydration, dan biayanya berskala dengan ukuran pohon interaktif Anda, bukan dengan seberapa banyak halaman yang benar-benar interaktif. Halaman yang sembilan puluh persen teks statis tetap membayar untuk hydration seluruhnya jika ia satu pohon React besar. Itulah pemborosan yang diserang kedua pendekatan.
Dalam model RSC setiap komponen adalah server component secara default. Ia berjalan sekali di server, bisa await data langsung, dan memancarkan deskripsi UI terserialisasi yang dirender klien tanpa pernah menerima JavaScript komponen itu sendiri. Anda menyeberang ke wilayah klien hanya saat menambahkan directive use client di bagian atas sebuah file. Semua dari batas itu ke bawah dikirim ke browser dan di-hydrate; semua di atasnya tidak. Pergeseran mentalnya adalah interaktivitas kini sesuatu yang Anda pilih secara eksplisit, file demi file.
// app/dashboard/page.tsx — a Server Component (no directive)
// Runs on the server, awaits data, ships NO JS for this file.
export default async function Dashboard() {
const invoices = await db.invoice.findMany({ take: 20 });
return (
<section>
<h1>Invoices</h1>
<InvoiceTable rows={invoices} /> {/* static, server-rendered */}
<RefreshButton /> {/* client island, see below */}
</section>
);
}
// app/dashboard/refresh-button.tsx
'use client'; // <- the hydration boundary
import { useRouter } from 'next/navigation';
export function RefreshButton() {
const router = useRouter();
return <button onClick={() => router.refresh()}>Refresh</button>;
}Letakkan use client di daun pohon Anda, bukan akar. Jika Anda menandai layout tingkat atas sebagai client component, setiap anak ikut tertarik ke bundle klien meskipun sebenarnya tidak perlu. Dorong directive sejauh mungkin ke bawah menuju elemen interaktif yang sebenarnya.
Astro membalik default ke arah lain. Sebuah halaman adalah HTML statis tanpa JavaScript klien sama sekali sampai Anda melakukan hydration pada komponen tertentu, sebuah island, dengan client directive. Directive itu juga mengontrol kapan hydration terjadi: client:load langsung berjalan, client:idle menunggu main thread tenang, client:visible menunggu komponen masuk viewport, dan client:media menunggu media query CSS. Setiap island terisolasi; mereka tidak berbagi runtime dan tidak bisa langsung saling berbicara, itulah trade-off yang disengaja agar mereka tetap kecil dan independen.
---
// index.astro — everything here is static HTML, zero JS
import Hero from '../components/Hero.astro';
import PriceCalculator from '../components/PriceCalculator.jsx';
import Newsletter from '../components/Newsletter.jsx';
---
<Hero />
<!-- Hydrate only when it scrolls into view -->
<PriceCalculator client:visible />
<!-- Hydrate after the browser is idle -->
<Newsletter client:idle />Qwik membawa ide islands ke ekstrem logisnya dengan resumability. Alih-alih melakukan hydration sama sekali, Qwik menserialisasi state aplikasi dan lokasi setiap event handler ke dalam HTML, lalu melanjutkan (resume) di klien tanpa mengeksekusi ulang kode komponen yang sudah dijalankan server. JavaScript untuk sebuah handler diambil hanya saat pengguna benar-benar memicunya. Hasilnya, eksekusi JavaScript awal tetap mendekati nol berapapun besarnya aplikasi, karena tidak ada replay hydration yang harus dibayar di muka.
| Dimensi | React Server Components | Islands (Astro / Qwik) |
|---|---|---|
| Rendering default | Server component; pilih klien dengan use client | HTML statis; pilih interaktivitas per island |
| Model hydration | Parsial — hanya batas klien yang di-hydrate | Astro: parsial per island. Qwik: tidak ada (resumability) |
| Baseline JS klien | Runtime React plus komponen interaktif | Astro: runtime per island. Qwik: mendekati nol di muka |
| Pengambilan data | Langsung di server component dengan await | Di frontmatter / loader, lalu diteruskan ke islands |
| State antar-komponen | Pohon React bersama; context bekerja lintas bagian klien | Islands terisolasi; berbagi butuh store atau event |
| Paling cocok | Produk mirip aplikasi, berbasis data, sangat interaktif | Situs konten, marketing, dokumentasi, blog dengan sedikit UI |
Jangan memilih arsitektur hanya berdasarkan benchmark. Resumability Qwik menukar biaya hydration dengan serialisasi state dan lazy fetching, yang menambah kompleksitas dan edge case tersendiri. RSC mendorong pekerjaan ke server, sehingga kapasitas server dan latensi lapisan data menjadi bottleneck baru. Ukur trade-off terhadap trafik dan tim Anda yang sebenarnya, bukan sebuah demo.
Tidak ada pemenang universal di sini, dan itu kesimpulan yang jujur. RSC dan islands adalah dua jawaban untuk masalah yang sama, dirumuskan dari default yang berlawanan: RSC mengasumsikan server-rendered dan meminta Anda membenarkan kode klien, sementara islands mengasumsikan statis dan meminta Anda membenarkan interaktivitas. Pilih default yang cocok dengan bentuk yang Anda bangun, dan Anda akan jauh lebih sedikit melawan framework Anda.