Skip to content

The Component Model

An Astro component is a .astro file you can import and use in another. It takes props and returns HTML. Because it renders on the server, a component is essentially a function from props to markup — with no client-side lifecycle.

src/components/Card.astro
---
interface Props {
title: string;
href: string;
}
const { title, href } = Astro.props; // props arrive on Astro.props
---
<a class="card" href={href}>
<h3>{title}</h3>
</a>

Using it:

---
import Card from '../components/Card.astro';
---
<Card title="Hello" href="/hello" />

Props come in through Astro.props, and you type them by declaring a Props interface in the frontmatter — Astro uses it to type-check usage. There is no useState here; a component runs once to produce HTML.

A component often needs to wrap content it doesn’t know in advance. That’s what slots are for — the child markup you put between the component’s tags is rendered where <slot /> appears.

src/components/Panel.astro
---
const { heading } = Astro.props;
---
<section class="panel">
<h2>{heading}</h2>
<slot /> <!-- children go here -->
</section>
<Panel heading="Notes">
<p>Any markup here lands in the slot.</p>
</Panel>

For more than one insertion point, use named slots:

Layout.astro
<header><slot name="header" /></header>
<main><slot /></main> <!-- the default slot -->
<footer><slot name="footer" /></footer>
<Layout>
<h1 slot="header">Title</h1>
<p>Body goes in the default slot.</p>
<small slot="footer">© 2026</small>
</Layout>
flowchart LR
  parent["Parent passes markup
between the tags"] --> slot["Component renders it
at <slot />"]
  slot --> out["composed HTML"]
Slots let a parent inject markup into a component

Slots are the backbone of layouts: a Layout.astro defines the page shell (head, nav, footer) with a <slot /> for the page body, and every page wraps its content in that layout.

A <style> tag inside a .astro component is scoped to that component by default — Astro rewrites the selectors so they don’t leak out or clash with other components.

<h1>Title</h1>
<style>
/* This h1 rule applies ONLY to this component's h1, not others on the page. */
h1 { color: rebeccapurple; }
</style>

You get component-local CSS with plain selectors — no CSS-in-JS, no BEM naming ceremony. When you do want a rule to apply globally, you opt out with <style is:global> (covered in the styles lesson).

How does an Astro component receive its props?
What is a slot used for?
By default, where does a `<style>` block in a .astro component apply?
How do you provide multiple insertion points in one component?