Server-Side Rendering dengan Astro: Integrasi Framework React dan Vue dalam Satu Proyek
Memahami Pendekatan SSR Astro dengan Arsitektur Zero-JS by Default
Astro menggunakan pendekatan yang berbeda dari framework modern lainnya. Secara default, Astro merender semua komponen menjadi HTML statis di server tanpa mengirim satu byte pun JavaScript ke browser. Konsep ini dikenal sebagai zero-JS by default, dan ini adalah fondasi utama yang membedakan Astro dari Next.js atau Nuxt.
Ketika kita mengaktifkan SSR mode melalui adapter seperti @astrojs/node atau @astrojs/vercel, Astro tetap mempertahankan filosofi yang sama. Semua komponen tetap di-render di server menjadi HTML, dan JavaScript hanya dikirim jika kita secara eksplisit memintanya melalui client directives. Artinya, SSR di Astro tidak sama dengan SSR di framework lain. Di sini, server-side rendering justru digunakan untuk mengurangi beban klien, bukan menambahkannya.
Yang membedakan Astro dari Next.js atau Nuxt adalah pendekatan framework-agnostik-nya. Next.js hanya mendukung React, Nuxt hanya mendukung Vue. Astro memiliki compiler sendiri yang mampu memproses komponen dari React, Vue, Svelte, dan Solid secara bersamaan dalam satu proyek. Compiler ini tidak menjalankan React atau Vue saat SSR. Sebaliknya, compiler Astro-lah yang menghasilkan HTML dari output framework mana pun. Ini berarti kita bisa menggabungkan komponen dari berbagai framework tanpa khawatir tentang konflik runtime di server.
// astro.config.mjs - Setup SSR dasar
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';
export default defineConfig({
output: 'server',
adapter: node({
mode: 'standalone'
})
});Konfigurasi di atas menginstruksikan Astro untuk berjalan dalam mode server. Properti output: 'server' memberi tahu Astro bahwa halaman akan di-render secara dinamis per permintaan, bukan sebagai file statis. Adapter @astrojs/node dengan mode standalone memungkinkan aplikasi berjalan sebagai server Node.js mandiri tanpa perlu framework tambahan seperti Express. Ketika kita menjalankan npm run build, Astro akan menghasilkan folder dist/ yang berisi server siap produksi beserta seluruh halaman yang sudah di-render.
Mengonfigurasi Integrasi React dan Vue dalam Satu Proyek Astro
Mengintegrasikan React dan Vue dalam satu proyek Astro hanya membutuhkan dua paket integrasi resmi. Pertama, kita instal @astrojs/react dan @astrojs/vue melalui npm. Setelah instalasi, kita tambahkan kedua integrasi tersebut ke dalam astro.config.mjs.
npm install @astrojs/react @astrojs/vueAstro secara otomatis mendeteksi framework berdasarkan ekstensi file. File dengan ekstensi .jsx atau .tsx akan diproses sebagai komponen React. File .vue akan diproses sebagai komponen Vue. Kita tidak perlu mengkonfigurasi loader terpisah atau plugin Babel. Astro menangani seluruh toolchain secara internal.
Di balik layar, Astro menggunakan mekanisme renderToString dari masing-masing framework untuk merender komponen ke HTML saat SSR. Untuk React, Astro memanggil ReactDOMServer.renderToString(). Untuk Vue, Astro memanggil renderToString() dari @vue/server-renderer. Kedua proses ini berjalan secara terpisah dan tidak saling mengganggu.
// astro.config.mjs - Integrasi React dan Vue
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import vue from '@astrojs/vue';
import node from '@astrojs/node';
export default defineConfig({
output: 'server',
adapter: node({
mode: 'standalone'
}),
integrations: [
react(),
vue()
]
});Dengan konfigurasi ini, Astro akan memproses komponen React dan Vue secara independen. Ketika build dijalankan, Astro mengkompilasi setiap komponen ke dalam HTML statis menggunakan compiler framework masing-masing. Hasilnya adalah halaman HTML lengkap tanpa JavaScript framework runtime di dalamnya, kecuali kita secara eksplisit menambahkan interaktivitas melalui client directives.
Mengontrol Perilaku Hydration dengan Client Directives
Salah satu fitur paling powerful dari Astro adalah client directives. Karena semua komponen di-render sebagai HTML statis saat SSR, kita perlu menentukan kapan dan bagaimana komponen tersebut akan di-hydrate di browser. Tanpa client directives, komponen React atau Vue hanya akan menjadi HTML statis. Tidak ada state, tidak ada event handler, tidak ada interaktivitas.
Astro menyediakan lima client directives yang masing-masing memiliki perilaku berbeda:
client:loadmenghidrasi komponen segera setelah halaman dimuat. Cocok untuk komponen yang harus langsung interaktif seperti navigasi utama atau form pencarian.client:idlemenunggu hingga browser dalam keadaan idle menggunakanrequestIdleCallback. Cocok untuk komponen yang tidak mendesak seperti panel statistik atau widget cuaca.client:visiblemenggunakan IntersectionObserver untuk menghidrasi komponen hanya ketika elemen terlihat di viewport. Ideal untuk konten di bawah fold seperti carousel gambar atau bagian testimoni.client:mediamenghidrasi komponen hanya ketika media query tertentu terpenuhi. Berguna untuk komponen yang hanya relevan di perangkat mobile atau desktop.
Fullstack Web Development With Next.js
A practical, beginner-friendly, and project-based introduction to full-stack web...
client:onlymelewatkan SSR sepenuhnya dan hanya merender di client. Digunakan untuk komponen yang bergantung pada browser APIs sepertilocalStorage,window, atau library grafis.
---
// index.astro
import Header from '../components/Header.jsx';
import Counter from '../components/Counter.vue';
import Chart from '../components/Chart.jsx';
---
<html>
<head><title>Astro Multi-Framework</title></head>
<body>
<!-- Langsung interaktif saat halaman dimuat -->
<Header client:load />
<!-- Hidrasi saat browser sedang idle -->
<Counter client:idle />
<!-- Hidrasi hanya ketika elemen terlihat -->
<Chart client:visible />
</body>
</html>Pemilihan client directive yang tepat berdampak langsung pada performa halaman. Mengirim terlalu banyak JavaScript klien akan memperlambat waktu muat dan meningkatkan Total Blocking Time (TBT). Sebaliknya, terlalu sedikit interaktivitas bisa membuat halaman terasa tidak responsif. Strategi yang baik adalah memulai dengan client:visible untuk semua komponen, lalu mengganti ke client:load hanya untuk komponen yang benar-benar membutuhkan interaksi segera. Kita bisa memverifikasi perilaku ini melalui browser DevTools pada tab Network. Kita akan melihat file JavaScript hanya dimuat ketika komponen dengan directive tertentu mulai di-hydrate.

Gambar: Ilustrasi arsitektur Islands — komponen interaktif (Header, Image carousel) berada sebagai pulau di tengah HTML statis, sementara konten lain tetap ringan tanpa JavaScript. — Sumber: [Astro Docs](https://docs.astro.build/en/concepts/islands/)
Menggabungkan Komponen React dan Vue dalam Satu Layout Halaman
Keunggulan utama Astro adalah kemampuan mengimpor dan merender komponen React dan Vue secara bersamaan dalam file .astro yang sama. Tidak diperlukan wrapper khusus, tidak perlu micro-frontend, tidak ada adaptor tambahan. Kita cukup mengimpor komponen dan menggunakannya dalam template.
Props mengalir dari komponen Astro (server) ke setiap komponen framework saat SSR. Ini berarti kita bisa mengirim data yang sama ke komponen React dan Vue, dan keduanya akan menerima props tersebut selama proses rendering server. Mari kita lihat contoh konkret dengan tiga file: komponen React, komponen Vue, dan halaman Astro yang menggabungkan keduanya.
// components/Header.jsx
export default function Header({ title, subtitle }) {
return (
<header>
<h1>{title}</h1>
<p>{subtitle}</p>
</header>
);
}<!-- components/Counter.vue -->
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="count++">Tambah</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const props = defineProps(['initialCount']);
const count = ref(props.initialCount || 0);
</script>---
// pages/index.astro
import Header from '../components/Header.jsx';
import Counter from '../components/Counter.vue';
const pageTitle = "Dashboard Multi-Framework";
const pageSubtitle = "React + Vue dalam satu halaman";
const initialValue = 5;
---
<html>
<body>
<Header title={pageTitle} subtitle={pageSubtitle} client:load />
<Counter initialCount={initialValue} client:load />
</body>
</html>Saat SSR berjalan, Astro merender komponen Header menggunakan ReactDOMServer dan komponen Counter menggunakan @vue/server-renderer. Keduanya menghasilkan HTML statis yang digabungkan dalam satu halaman. Setelah halaman dimuat di browser, React menghidrasi komponen Header dan Vue menghidrasi komponen Counter. Masing-masing framework hanya mengontrol komponennya sendiri. Satu hal penting yang perlu diingat: data mengalir satu arah dari Astro ke komponen framework. Jika kita membutuhkan komunikasi dua arah antar framework, kita memerlukan pola state management yang berbeda.
Strategi Berbagi State Lintas Framework dengan Nanostores
Ketika kita memiliki komponen React dan Vue yang perlu berbagi state yang sama, Astro merekomendasikan nanostores sebagai solusi resmi untuk cross-framework reactivity. Nanostores adalah library reactive store berukuran sangat kecil (di bawah 1KB) yang bekerja dengan React melalui @nanostores/react dan Vue melalui @nanostores/vue.
Konsepnya sederhana: kita definisikan store sebagai file .ts terpisah, lalu komponen dari framework mana pun bisa mengimpornya. Store menggunakan primitive reactive seperti atom atau map yang bisa dibaca dan ditulis dari mana saja.
// stores/counter.ts
import { atom } from 'nanostores';
export const countStore = atom(0);
export function increment() {
countStore.set(countStore.get() + 1);
}
export function decrement() {
countStore.set(countStore.get() - 1);
}Komponen React membaca store melalui hook useStore dari @nanostores/react. Hook ini secara otomatis mendaftarkan komponen sebagai subscriber. Setiap kali store berubah, komponen akan re-render.
// components/ReactCounter.jsx
import { useStore } from '@nanostores/react';
import { countStore, increment, decrement } from '../stores/counter';
export default function ReactCounter() {
const count = useStore(countStore);
return (
<div>
<h3>React Counter</h3>
<p>Count: {count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
);
}Di sisi Vue, kita menggunakan hook useStore yang sama dari @nanostores/vue. Perbedaannya hanya pada package impor. API penggunaannya identik.
<!-- components/VueCounter.vue -->
<template>
<div>
<h3>Vue Counter</h3>
<p>Count: {{ count }}</p>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
<script setup>
import { useStore } from '@nanostores/vue';
import { countStore, increment, decrement } from '../stores/counter';
const count = useStore(countStore);
</script>Dengan pola ini, ketika pengguna menekan tombol "+" di komponen React, store akan berubah dan komponen Vue akan otomatis menampilkan nilai terbaru. Mekanismenya sederhana: nanostores menggunakan pub-sub pattern. Setiap komponen yang memanggil useStore akan mendaftarkan diri sebagai subscriber. Ketika countStore.set() dipanggil, semua subscriber, baik React maupun Vue, akan menerima notifikasi dan melakukan re-render. Tidak perlu context, provider, atau event emitter yang rumit. Pendekatan ini juga memudahkan testing karena store terpisah dari komponen.
Pertimbangan Performa dan Deployment untuk SSR Multi-Framework
Setiap framework yang digunakan akan menambahkan runtime-nya ke bundle klien saat hydration digunakan. React mengirimkan react-dom runtime sekitar 40KB terkompresi, Vue sekitar 20KB terkompresi. Meskipun tidak besar, ukuran ini akan bertambah jika kita menggunakan kedua framework secara bersamaan di halaman yang sama.
Kita harus selektif dalam memilih client directives. Gunakan client:load hanya untuk komponen yang langsung terlihat dan membutuhkan interaksi. Untuk konten di bawah fold, preferensi kita adalah client:visible atau client:idle. Dengan strategi ini, runtime framework hanya diunduh ketika benar-benar diperlukan. Pengguna yang membuka halaman dan tidak melakukan scroll tidak perlu mengunduh runtime Vue jika komponen Vue berada di bagian bawah halaman.
Astro secara otomatis melakukan code-splitting per framework. Runtime React dan Vue akan dimuat dalam chunk terpisah. Pengguna tidak perlu mengunduh runtime kedua framework jika hanya satu yang digunakan di halaman tertentu. Ini adalah keuntungan signifikan dibandingkan pendekatan SPA tradisional di mana seluruh bundle aplikasi harus diunduh sebelum halaman bisa berfungsi.
Untuk deployment, SSR memerlukan adapter yang sesuai. Pilihan adapter bergantung pada platform target:
@astrojs/nodeuntuk server Node.js tradisional atau Docker container.@astrojs/verceluntuk platform serverless Vercel dengan optimasi otomatis.@astrojs/netlifyuntuk Netlify Functions dengan integrasi edge.
Penting untuk diingat bahwa mode output: 'static' tidak kompatibel dengan fitur SSR. Kita harus menggunakan output: 'server' agar halaman bisa di-render secara dinamis per permintaan. Performa cold-start pada platform serverless sangat bergantung pada pilihan adapter dan ukuran runtime framework. Untuk aplikasi multi-framework, pertimbangkan menggunakan adapter Node.js dengan long-running server untuk menghindari cold-start latency.
Tertarik menguasai Astro dan arsitektur web modern secara mendalam? Bergabunglah dengan program bootcamp Rumah Coding untuk belajar langsung dari praktisi industri dan bangun portofolio nyata dengan teknologi terkini.
Kursus Terkait
Fullstack Web Development With Next.js
A practical, beginner-friendly, and project-based introduction to full-stack web development. Students will learn to build, secure, and deploy modern web applications from scratch using Next.js (App Router), React, Tailwind CSS, and a relational database. By the end of the course, students will have a fully functional, production-ready application to showcase in their portfolio.
TechConnect - Modern IT Job Portal
- Public Job Board: A responsive homepage displaying available job listings with dynamic routing for individual job detail pages.
- Search & Filter: Basic functionality allowing users to find jobs based on keywords or categories.
- User Authentication: Secure Sign Up, Log In, and Log Out workflows.
MERN Stack Development
Launch your journey into full-stack web development with this comprehensive, project-driven course. Designed for beginners, this course demystifies the MERN stack (MongoDB, Express.js, React.js, Node.js) by guiding you step-by-step in building a real-world application from scratch. By the end of this course, you will have the practical skills and a complete portfolio project to confidently step into the modern web development industry.
EduStream - Mini Learning Management System (LMS)
- Secure Authentication & Authorization: Robust user registration and login using JWT, with strict role-based access control (Admin/Instructor vs. Student).
- Course Management (Admin Dashboard): Full CRUD (Create, Read, Update, Delete) capabilities for administrators to manage course details, including titles, descriptions, pricing, and thumbnail image uploads.
- Public Course Catalog: An interactive and responsive storefront where users can browse available courses.