Passing Astro components to React components
By Flavio Copes
Learn how to pass Astro components into a React island using named slots, then render them inside the component through props.pro and props.free.
You can pass Astro components to a React component using named slots. Astro renders the slotted content and hands it to the React component as props, one prop per slot name.
Here’s how I found this out.
In an Astro site page I wanted to add some bit of interactivity, and chose React.
I created the component, and inside it I had a pro state variable that was true or false and showed different things based on this state:
import { useState } from 'react'
export default function TabBar() {
const [pro, setPro] = useState(false)
return (
<div>
{pro ? <p>pro</p> : <p>free</p>}
</div>
)
}
and I used it like this:
<TabBar client:load />
(client:load otherwise it’s server-rendered at build time and not interactive).
So far so good.
Passing Astro components through slots
But I wanted to pass multiple Astro components to this React component. Hardcoding their content inside the React component wasn’t an option, since Free was an Astro component with its own markup.
Here’s what I did:
<TabBar client:load>
<div slot='free'>
<Free />
</div>
<div class='pt-2 mb-20' slot='pro'>PRO</div>
</TabBar>
Each child gets a slot attribute with a name. Astro renders those children on the server, then exposes each one to the React component as a prop with the same name.
Inside the React component, those slots are available through {props.pro} and {props.free}:
import { useState } from 'react'
export default function TabBar(props) {
const [pro, setPro] = useState(false)
return (
<div>
{pro ? props.pro : props.free}
</div>
)
}
The React state still drives which one is shown. The Astro components provide the content.
If you don’t need multiple slots, there’s an even shorter path: put a single child inside the component with no slot attribute, and it shows up as props.children, like regular React children.
One thing to know
The slotted content is rendered by Astro at build time. What React receives is the finished output, not a live component.
This means you can’t pass React state or props down into <Free />. If the content inside a slot needs to react to state changes, it has to be part of the React component itself (or be its own island).
Also, pick slot names that are valid JavaScript identifiers. A slot named pro-tab forces you to write props['pro-tab'] instead of the cleaner props.proTab.
Related posts about astro: