Skip to content

Importing Components

Composing components in Astro works exactly as you expect from React. Import in the frontmatter, use in the template. The import syntax is standard ES module syntax — the only requirement is that the import lives inside the --- frontmatter block.

React
// src/pages/Home.jsx
import Hero from '../components/Hero';
import Footer from '../components/Footer';
export default function Home() {
return (
<>
<Hero title="Welcome" subtitle="Built with React" />
<main>
<p>Page content here.</p>
</main>
<Footer />
</>
);
}
Astro
---
// src/pages/index.astro
import Hero from '../components/Hero.astro';
import Footer from '../components/Footer.astro';
---
<Hero title="Welcome" subtitle="Built with Astro" />
<main>
<p>Page content here.</p>
</main>
<Footer />

Key differences:

  • Astro imports always go between the --- fences, not at the top of the file.
  • .astro is a required extension in the import path (unlike React’s .jsx/.tsx which bundlers often resolve automatically).
  • An Astro page does not need a root wrapper element — the template is a document fragment.

Composition works identically to React — pass props, pass slots, nest as deeply as you need. An Astro component can import and use other Astro components, React components (as islands), Svelte components, and more.

React
// PageLayout.jsx
import Header from './Header';
import Sidebar from './Sidebar';
import Card from './Card';
export default function PageLayout({ posts }) {
return (
<div className="layout">
<Header />
<div className="content">
<Sidebar />
<main>
{posts.map((post) => (
<Card key={post.id} title={post.title} />
))}
</main>
</div>
</div>
);
}
Astro
---
// PageLayout.astro
import Header from './Header.astro';
import Sidebar from './Sidebar.astro';
import Card from './Card.astro';
const { posts } = Astro.props;
---
<div class="layout">
<Header />
<div class="content">
<Sidebar />
<main>
{posts.map((post) => (
<Card title={post.title} />
))}
</main>
</div>
</div>

Note on the runnable snippet: The StackBlitz playground runs a single src/pages/index.astro file. The example below inlines the “sub-component” markup directly to stay self-contained. In a real project you would split it into separate .astro files and import them.

Astro
---
// Self-contained page — no separate component files needed
const team = [
  { name: "Alice", role: "Engineer" },
  { name: "Bob", role: "Designer" },
  { name: "Carol", role: "PM" },
];
---
<html lang="en">
  <head><meta charset="utf-8" /><title>Team</title></head>
  <body style="font-family:sans-serif;padding:2rem">
    <header style="border-bottom:1px solid #e2e8f0;padding-bottom:1rem;margin-bottom:2rem">
      <h1>Our Team</h1>
    </header>
    <ul style="list-style:none;padding:0;display:grid;gap:1rem">
      {team.map((member) => (
        <li style="border:1px solid #e2e8f0;border-radius:8px;padding:1rem">
          <strong>{member.name}</strong>
          <span style="color:#888;margin-left:.5rem">{member.role}</span>
        </li>
      ))}
    </ul>
  </body>
</html>
Where must `import` statements for components appear in an .astro file?
Which import path is correct when using an .astro component?
Does importing and using an .astro component add JavaScript to the browser bundle?