@astrojs/react, @astrojs/svelte, and @astrojs/vue are official integrations that allow developers to use popular UI frameworks within Astro's island architecture. Unlike traditional meta-frameworks that render the entire app on the server or client, these packages enable you to hydrate specific interactive components while keeping the rest of the page as static HTML. @astrojs/react brings the vast React ecosystem to Astro, supporting both legacy class components and modern hooks. @astrojs/svelte compiles Svelte components at build time, offering a unique approach where the framework code itself is compiled away, leaving only the necessary logic. @astrojs/vue allows Vue 3 developers to leverage the Composition API and Options API within Astro, supporting features like Teleport and Suspense. All three packages provide directives to control exactly when and how components load on the client.
Astro has changed how we think about frontend architecture by popularizing the "Islands Architecture." Instead of sending a heavy JavaScript bundle for the whole page, Astro sends static HTML and only hydrates specific interactive parts. The packages @astrojs/react, @astrojs/svelte, and @astrojs/vue are the bridges that let you use your favorite tools inside these islands. While they all solve the same problem, they differ significantly in how they compile code, manage state, and impact performance.
One of Astro's most powerful features is the ability to choose when a component loads. All three integrations support the same set of hydration directives. This means whether you use React, Svelte, or Vue, you control the loading behavior in the exact same way in your .astro files.
@astrojs/react uses standard JSX syntax. You import the component and apply a directive like client:load to hydrate it immediately on the browser.
---
// src/pages/index.astro
import Counter from '../components/ReactCounter.jsx';
---
<html>
<body>
<!-- Hydrates immediately on page load -->
<Counter client:load />
</body>
</html>
@astrojs/svelte works identically in the Astro template, even though the component file extension is .svelte. The directive handles the heavy lifting of mounting the Svelte app.
---
// src/pages/index.astro
import SvelteCounter from '../components/SvelteCounter.svelte';
---
<html>
<body>
<!-- Hydrates when the user scrolls the element into view -->
<SvelteCounter client:visible />
</body>
</html>
@astrojs/vue follows the same pattern for .vue files. You can mix static Astro content with dynamic Vue islands seamlessly.
---
// src/pages/index.astro
import VueCounter from '../components/VueCounter.vue';
---
<html>
<body>
<!-- Hydrates only after interaction (e.g., click) -->
<VueCounter client:idle />
</body>
</html>
The biggest technical difference lies in what code actually reaches the user's browser. This affects load times and memory usage.
@astrojs/react ships the React runtime. Even if your component is small, the browser must download and parse the React library to render it. This is a trade-off: you get a stable, familiar API, but you pay a "tax" in bundle size for every island.
// ReactCounter.jsx
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
// React needs the runtime to manage this state
return (
<button onClick={() => setCount(count + 1)}>
Count is {count}
</button>
);
}
@astrojs/svelte compiles away the framework. There is no "Svelte runtime" in the traditional sense. The compiler turns your component into efficient, imperative JavaScript that directly manipulates the DOM. This usually results in the smallest bundle size.
<!-- SvelteCounter.svelte -->
<script>
let count = 0;
// Svelte compiles this into direct DOM updates
function increment() {
count += 1;
}
</script>
<button on:click={increment}>
Count is {count}
</button>
@astrojs/vue ships the Vue runtime, similar to React. However, Vue 3 is highly modular. If you use the Composition API and tree-shaking is configured correctly, you can keep the runtime overhead reasonable, though it is generally heavier than Svelte's output.
<!-- VueCounter.vue -->
<script setup>
import { ref } from 'vue';
const count = ref(0);
// Vue needs the reactivity system runtime
function increment() {
count.value++;
}
</script>
<template>
<button @click="increment">
Count is {{ count }}
</button>
</template>
How you manage data changes varies by framework, which impacts how you structure your logic within an Astro island.
@astrojs/react relies on hooks. State is immutable, and updates trigger re-renders of the component tree. This model is predictable but requires careful handling of dependencies to avoid unnecessary renders.
// React: Explicit dependency arrays
useEffect(() => {
console.log('Count changed');
}, [count]);
@astrojs/svelte uses compiled reactivity. You don't need dependency arrays. The compiler analyzes your code and inserts update logic wherever variables change. This leads to less boilerplate.
<!-- Svelte: Automatic reactivity -->
<script>
$: console.log('Count changed', count); // Runs automatically when count changes
</script>
@astrojs/vue offers two styles. The Options API groups logic by feature (data, methods), while the Composition API (shown above) groups logic by concern, similar to React hooks. Vue's reactivity system tracks dependencies automatically at runtime, so you rarely need manual dependency arrays like in React.
<!-- Vue: Watchers handle side effects -->
<script setup>
import { watch } from 'vue';
watch(count, (newVal) => {
console.log('Count changed', newVal);
});
</script>
Astro components can pass data down to framework components. The syntax differs slightly to match each framework's conventions.
@astrojs/react passes props as standard JSX attributes. Children work as expected.
---
import Card from '../components/Card.jsx';
const title = "Welcome";
---
<Card title={title}>
<p>Static content from Astro</p>
</Card>
// Card.jsx
export default function Card({ title, children }) {
return <div><h2>{title}</h2>{children}</div>;
}
@astrojs/svelte uses standard HTML attributes for props. It also supports named slots, which map cleanly to Astro's default slot syntax.
---
import SvelteCard from '../components/SvelteCard.svelte';
---
<SvelteCard title="Welcome">
<p>Static content from Astro</p>
</SvelteCard>
<!-- SvelteCard.svelte -->
<script>
export let title;
</script>
<div>
<h2>{title}</h2>
<slot></slot>
</div>
@astrojs/vue handles props similarly. Vue's slot system is powerful, allowing for scoped slots, though basic usage looks very similar to React and Svelte when coming from Astro.
---
import VueCard from '../components/VueCard.vue';
---
<VueCard title="Welcome">
<p>Static content from Astro</p>
</VueCard>
<!-- VueCard.vue -->
<script setup>
defineProps(['title']);
</script>
<template>
<div>
<h2>{{ title }}</h2>
<slot></slot>
</div>
</template>
Your choice often comes down to what your team already knows and what libraries you need.
@astrojs/react gives you access to the largest ecosystem. If you need a specific date picker, map library, or complex data grid, it likely exists for React. The learning curve is low if you already know React, but debugging hydration mismatches between server-rendered HTML and client React can sometimes be tricky.
@astrojs/svelte offers a delightful developer experience with minimal boilerplate. The lack of a virtual DOM means debugging is often more direct, as you are looking at real DOM nodes. However, the ecosystem, while growing fast, is smaller than React's. You might find fewer pre-built complex widgets.
@astrojs/vue strikes a balance. It has a massive ecosystem, nearly rivaling React, especially for enterprise tools. The tooling (like Vue Devtools) is excellent. If your team likes the structure of Vue or is migrating from Nuxt, this is the natural path. Note that you must ensure your Vue version is compatible with Astro's current SSR requirements.
| Feature | @astrojs/react | @astrojs/svelte | @astrojs/vue |
|---|---|---|---|
| Runtime Size | Heavy (ships React) | Tiny (compiled away) | Medium (ships Vue) |
| Reactivity Model | Hooks + Immutable State | Compiled Variables | Reactive Proxies |
| Syntax | JSX | HTML + Script | Template + Script |
| Ecosystem | Massive | Growing | Large |
| Best For | Existing React teams | Performance focus | Vue shops / Complex UIs |
There is no single "best" package among these three. They are all first-class citizens in the Astro ecosystem.
Pick @astrojs/react if you want to leverage the sheer volume of existing React libraries and your team is already fluent in hooks and JSX. It is the safe, standard choice for many.
Pick @astrojs/svelte if you care deeply about performance metrics and bundle size. It is perfect for small, highly interactive islands where every kilobyte counts, and for developers who hate boilerplate.
Pick @astrojs/vue if you are a Vue shop or need the specific capabilities of the Vue reactivity system and ecosystem. It is ideal for complex dashboards or applications migrating from Nuxt to Astro.
Final Thought: The power of Astro is that you don't have to pick just one for the whole site. You can use React for a complex checkout widget, Svelte for a lightweight theme toggle, and Vue for a user dashboard—all on the same page. Choose the right tool for each specific island.
Choose @astrojs/react if your team already has deep expertise in React or if you need to reuse an existing library of React components. It is the best fit when you require access to the massive React ecosystem, including specialized hooks, state management tools like Redux or Zustand, and UI kits like MUI or Chakra. Be aware that you will be shipping the React runtime to the client for every hydrated island, which can add to your bundle size compared to other options.
Choose @astrojs/svelte if you prioritize small bundle sizes and high performance without sacrificing developer experience. Since Svelte compiles components into vanilla JavaScript at build time, it does not require a heavy runtime library on the client, making it ideal for performance-critical interactive elements. This is also the top choice if you prefer writing less boilerplate code and enjoy Svelte's reactive syntax that feels like an extension of standard HTML and JavaScript.
Choose @astrojs/vue if your organization standardizes on Vue 3 or if you need to migrate existing Vue components into an Astro project. It is particularly strong for developers who rely on the Composition API for logic reuse or need Vue-specific features like Teleport for modals and Suspense for async component loading. Select this package when you want the structure and tooling of the Vue ecosystem while benefiting from Astro's static site generation capabilities.
This Astro integration enables server-side rendering and client-side hydration for your React components.
Read the @astrojs/react docs
Get help in the Astro Discord. Post questions in our #support forum, or visit our dedicated #dev channel to discuss current development and more!
Check our Astro Integration Documentation for more on integrations.
Submit bug reports and feature requests as GitHub issues.
This package is maintained by Astro's Core team. You're welcome to submit an issue or PR! These links will help you get started:
MIT
Copyright (c) 2023–present Astro