Passing Props to Islands
Passing props to a React island looks identical to passing props in JSX — the syntax is the same. There is one important constraint though: props passed across the Astro/island boundary must be serializable to JSON.
Serializable props
Section titled “Serializable props”Astro renders your page on the server. When it encounters a hydrated island, it serializes the props into the HTML so the browser can rehydrate the component with the exact same data.
That means props must be values JSON can represent:
- Strings, numbers, booleans
- Plain objects and arrays (no class instances)
nullandundefined
Not serializable: functions, Dates (use ISO strings instead), class instances, React nodes, DOM elements.
// React app — any prop type works,// including functions passed from a parent.<Counter start={10} label="Score" onReset={() => console.log('reset')} date={new Date()}/>---// Astro — props must be JSON-serializable.// Functions and class instances are NOT allowed// across the Astro→island boundary.import Counter from '../components/Counter.jsx';const iso = new Date().toISOString(); // Date → string ✓---<Counter start={10} label="Score" date={iso}/>{/* onReset={() => ...} would throw a build error */}Functions as props: You cannot pass a callback from
.astroto a React island. If your island needs a callback (e.g., a modal that callsonClose), handle that interaction entirely inside the island — fetch data, manage state, and navigate using the browser’s native APIs from within React.
Passing children / slots
Section titled “Passing children / slots”React passes children via the children prop. Astro uses <slot />. When you use a React island as a wrapper, you can pass HTML content to it via Astro’s slot mechanism — but the child content will be static HTML, not a React component tree.
---import Card from '../components/Card.jsx';---<Card client:load title="Hello"> <p>This content is passed as a slot — static HTML inside the React component.</p></Card>Inside Card.jsx, that content arrives as children exactly as you would expect:
export default function Card({ title, children }) { return ( <div className="card"> <h2>{title}</h2> <div>{children}</div> </div> );}Named slots
Section titled “Named slots”Astro supports named slots. Inside a React island, named slot content arrives as a prop with the slot name:
---import Modal from '../components/Modal.jsx';---<Modal client:load> <h2 slot="header">Confirm deletion</h2> <p>This action cannot be undone.</p> <button slot="footer">Cancel</button></Modal>export default function Modal({ children, header, footer }) { return ( <dialog> <header>{header}</header> <main>{children}</main> <footer>{footer}</footer> </dialog> );}Full comparison
Section titled “Full comparison”// React parent → React child// Any prop type, including functions and Dates.function Page() { return ( <UserCard user={userObject} // class instance ok onEdit={() => setEdit(true)} // function ok createdAt={new Date()} // Date ok /> );}---// Astro page → React island// Only JSON-serializable props allowed.import UserCard from '../components/UserCard.jsx';const user = await fetch('/api/user').then(r => r.json());---<UserCard name={user.name} avatarUrl={user.avatarUrl} createdAt={user.createdAt}/>{/* user object (plain JSON) ✓ ISO date string ✓ Function props ✗ — handle inside the island*/}