Skip to content

Framework Integrations

Astro doesn’t have its own client-side component runtime — instead it lets you use the UI framework you already know for the interactive bits. React, Vue, Svelte, Solid, and Preact all work as islands, via official integrations.

You add an integration with the CLI, which installs the package and wires up the config:

Terminal window
npx astro add react

That updates astro.config.mjs for you:

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

Now a .jsx component can be imported into an .astro file and hydrated with a client directive.

Importing a framework component as an island

Section titled “Importing a framework component as an island”

The island is written in the framework’s normal style; the .astro file imports it and decides when it hydrates.

// src/components/Counter.jsx — a plain React component
import { useState } from 'react';
export default function Counter({ start }) {
const [count, setCount] = useState(start);
return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
}
src/pages/index.astro
---
import Counter from '../components/Counter.jsx'; // 1. import the framework component
---
<h1>Welcome</h1>
<Counter start={0} client:visible /> <!-- 2. hydrate it as an island -->

Without the client:visible, that Counter would render as static HTML (the button, but no working onClick). The directive is what ships its JavaScript.

Because each island is independent, you can use different frameworks on the same page — a React widget next to a Svelte one — as long as both integrations are installed. This is genuinely useful for incremental migration or for dropping in a component that only exists in one ecosystem.

flowchart TB
  page["Astro page (static HTML)"]
  page --> r["React island — client:load"]
  page --> s["Svelte island — client:visible"]
  page --> v["Vue island — client:idle"]
One page, islands from different frameworks

When you pass props from an .astro file into an island, they are rendered on the server and serialized into the HTML so the client can rehydrate with the same values. That imposes one hard rule: island props must be serializable — strings, numbers, booleans, arrays, plain objects. What you cannot pass:

  • Functions (including event handlers). There’s no way to serialize a function across the server/client boundary.
  • Class instances, Date objects in some cases, and other non-plain values.
---
import Chart from '../components/Chart.jsx';
const data = await getMetrics(); // fetched on the server
---
<Chart data={data} client:visible /> <!-- fine: data is a plain array/object -->
<!-- <Chart onPoint={(p) => ...} /> — NOT allowed: functions cannot be serialized -->

If you need behavior instead of data, put that logic inside the island, or pass server-rendered children through a slot (covered next lesson). The framing that keeps you out of trouble: an .astro file hands an island data, not callbacks.

How do you add support for React components in an Astro project?
Can you use a React island and a Svelte island on the same page?
Why can't you pass a function (like an event handler) as a prop to an island from an .astro file?
A framework component imported into an .astro file with NO client directive will: