Props and Slots
Passing data into components with props and passing markup into them with slots.
2 menit membaca
Reusable components need two things: a way to receive data, and a way to receive markup from whoever is using them. Astro handles these with props and slots — concepts that will feel familiar if you've used any component-based framework.
Props: passing data in
Props are the attributes you pass when using a component, read inside its frontmatter via Astro.props.
---
// src/components/Badge.astro
interface Props {
label: string;
color?: string;
}
const { label, color = "gray" } = Astro.props;
---
<span class="badge" style={`background: ${color}`}>{label}</span>---
// src/pages/index.astro
import Badge from "../components/Badge.astro";
---
<Badge label="New" color="green" />
<Badge label="Draft" />The interface Props declaration is optional but worth adopting early — with TypeScript, Astro will flag a missing or misspelled prop at build time instead of letting it silently render as undefined. Notice color has a default ("gray") via destructuring, so the second <Badge> above renders without ever setting one explicitly.
Slots: passing markup in
Props carry data, but sometimes what you want to hand a component is markup itself — arbitrary child content the component doesn't need to know about in advance. That's what <slot /> is for.
---
// src/components/Card.astro
---
<div class="card">
<slot />
</div>---
import Card from "../components/Card.astro";
---
<Card>
<h2>Card Title</h2>
<p>Any markup placed here replaces the <slot /> in Card.astro.</p>
</Card>Whatever is written between <Card> and </Card> gets rendered exactly where <slot /> appears inside Card.astro. This is how layouts work under the hood, and it's the pattern behind any "wrapper" component — a modal shell, a page section, a styled container — that shouldn't need to know what's inside it.
Named slots
A component can expose more than one insertion point using named slots, useful when a component has distinct regions — a header and a body, say:
---
// src/components/Panel.astro
---
<div class="panel">
<header><slot name="header" /></header>
<div class="panel-body"><slot /></div>
</div><Panel>
<h3 slot="header">Panel Title</h3>
<p>This goes into the default (unnamed) slot.</p>
</Panel>Content tagged with slot="header" goes to the matching named slot; anything without a slot attribute falls into the plain <slot />. This lets one component define several distinct content areas while still letting the calling page decide what actually fills them — the same separation of concerns that props give you for data, extended to markup.