Using React, Vue, or Svelte Components Inside Astro

Chapter 8
Using React, Vue, or Svelte Components Inside Astro
Integrations, the serializable-props boundary, and how cross-framework composition actually works

This chapter builds directly on this site's own existing react1react4, vue1, and svelte1 courses — it covers the integration boundary between Astro and those frameworks, not the frameworks' own syntax, which those courses already teach in full.

Adding a Framework Integration

npx astro add react npx astro add vue npx astro add svelte
// astro.config.mjs import { defineConfig } from 'astro/config'; import react from '@astrojs/react'; import vue from '@astrojs/vue'; import svelte from '@astrojs/svelte'; export default defineConfig({ integrations: [react(), vue(), svelte()], });

npx astro add [framework] installs the needed packages and wires up astro.config.mjs automatically.

A genuinely unusual fact: all three can coexist in one project
Every other framework covered in this series requires committing to exactly one — a React app is React, a Vue app is Vue. Astro genuinely doesn't: react(), vue(), and svelte() can all be active integrations in the same project simultaneously, with a single page importing a .jsx React component, a .vue Vue component, and a .svelte Svelte component side by side, each hydrating independently as its own island (Chapter 7).

Using a Framework Component

--- import ReactCounter from '../components/ReactCounter.jsx'; --- <ReactCounter initialCount={5} client:load />

Props are passed as ordinary attributes, the same as any Astro component. Each framework's own file extension (.jsx/.tsx, .vue, .svelte) is imported directly — no wrapping needed.

A Real Constraint: Props Must Be Serializable

Not exactly "just like normal props"
Props crossing from Astro's own frontmatter into a framework island are serialized to cross the server-render boundary — plain data (strings, numbers, arrays, plain objects) survives correctly, but a function or a class instance passed as a prop does not transfer the way it would inside a pure React or Vue app. Any event handling logic needs to live inside the framework component itself, not be passed in from the Astro side as a callback prop.

A Second Real Constraint: No Direct Cross-Framework Nesting

A React component cannot directly contain a Vue component as a JSX child, and vice versa — the two framework runtimes don't compose that way. Composition across frameworks happens at the Astro layer instead: an Astro component can wrap a framework island and use <slot /> (Chapter 4) to let Astro-level content — including other framework islands — surround it.

--- // src/components/Panel.astro --- <div class="panel"> <slot /> </div>
--- import Panel from '../components/Panel.astro'; import ReactCounter from '../components/ReactCounter.jsx'; import VueToggle from '../components/VueToggle.vue'; --- <Panel> <ReactCounter client:load /> <VueToggle client:load /> </Panel>

Both islands sit side by side inside Panel's own <slot /> — real, valid composition, achieved entirely at the Astro layer rather than one framework component containing the other directly.

For the framework syntax itself
This chapter deliberately stops at the integration boundary. For everything about how React's own hooks, Vue's own reactivity, or Svelte's own runes actually work, this site's existing react1react4, vue1, and svelte1 courses already cover that ground in full.

Coding Challenges

Challenge 1

Add the React integration, then use a React component in an Astro page passing a plain numeric prop (e.g. initialCount={5}), confirming the value is received correctly.

📄 View solution
Challenge 2

Demonstrate the correct way to give a React island its own click handler — defined inside the component itself, not passed in as a function prop from Astro's frontmatter.

📄 View solution
Challenge 3

Build an Astro wrapper component with a <slot />, and use it to compose a React island and a Svelte (or Vue) island side by side inside it.

📄 View solution

Chapter 8 Quick Reference

  • npx astro add [react|vue|svelte] — installs and configures a framework integration
  • Multiple integrations can coexist — a genuinely unusual trait among frameworks in this series
  • Props are serialized — plain data survives; functions/class instances don't cross the boundary as props
  • No direct cross-framework nesting — composition happens via an Astro wrapper's own <slot />
  • This chapter stops at the boundary — see react1react4/vue1/svelte1 for the frameworks themselves
  • Next chapter: Data Fetching & API Endpoints