Overview
Every frontend framework is based on the concept of Components: Vue, Angular, React use this powerful feature to build complete applications.
Astro is no exception, and in this article we will see what Components is and how to use it in an Astro project.
Let's start! 🤙
What are Astro Components?
Astro Components are reusable pieces of code and they are HTML-only templating components with no client-side runtime.
The extension of these files is .astro
.
Astro components are extremely flexible because you can use them in different ways:
- It can contain some reusable UI on the page like footer or navbar.
- It may contain a collection of common
<meta>
tags that make SEO easy to work with. - It can even contain an entire page layout.
It is important to know that Astro Components do not render on the client, it's possible include JavaScript code inside of your component frontmatter, and all of it will be stripped from the final page sent to your use's browsers.
The Structure
An Astro Component is made up of two main parts: script and the template.
---
// Component Script (JavaScript or Typescript code)
---
<!-- Component Template (HTML + JS Expressions) -->
You can use the script to write any JavaScript code that you need to render your template, for example:
- importing other Astro components
- importing other framework components, like Vue
- importing data, like a JSON file
- fetching content from an API or database
import SomeAstroComponent from '../components/SomeAstroComponent.astro';
import SomeReactComponent from '../components/SomeVueComponent.vue';
import someData from '../data/data.json';
const { title } = Astro.props;
const data = await fetch('example/posts').then(r => r.json());
This approach is highly secure because at build time, the JavaScript code will not end up in the end user's browser.
Instead the template determines the HTML output of your component: if you write plain HTML here, your component will render that HTML in any Astro page it is imported and used.
Astro’s component template syntax also supports JavaScript expressions, <style>
and <script>
tags, imported components, and special Astro directives.
Data and values defined in the component script can be used in the component template to produce dynamically-created HTML, like Vue or React.
---
// Your component script here!
import Banner from '../components/Banner.astro';
import VueCardComponent from '../components/VueCardComponent.vue';
const myFavoriteFruits = [/* ... */];
const { title } = Astro.props;
---
<Banner />
<h1>Hello, Astro!</h1>
<p>{title}</p>
<VueCardComponent client:visible />
<ul>
{myFavoriteFruits.map((data) => <li>{data.name}</li>)}
</ul>
<p class:list={["add", "dynamic", {classNames: true}]} />
Props
An Astro component can define and accept props. These props then become available to the component template for rendering HTML. Props are available on the Astro.props
global in your script.
Declaration:
---
const { greeting, name } = Astro.props;
---
<h2>{greeting}, {name}!</h2>
Usage:
<GreetingHeadline greeting="Hello" name="World" />
It's possible define your props with TypeScript with a Props
type interface. Astro will automatically pick up the Props interface in your frontmatter and give type warnings/errors.
---
interface Props {
name: string;
greeting?: string;
}
const { greeting = "Hello", name } = Astro.props;
---
<h2>{greeting}, {name}!</h2>
Slots
The <slot />
element is a placeholder for external HTML content, allowing you to inject child elements from other files into your component template.
By default, all child elements passed to a component will be rendered in its <slot />
.
For example:
---
import Header from './Header.astro';
import Logo from './Logo.astro';
import Footer from './Footer.astro';
const { title } = Astro.props;
---
<div id="content-wrapper">
<Header />
<Logo />
<h1>{title}</h1>
<slot />
<Footer />
</div>
---
import Wrapper from '../components/Wrapper.astro';
---
<Wrapper title="John's Page">
<h2>All about John</h2>
<p>Here is some stuff about John.</p>
</Wrapper>
Named Slots
An Astro component can also have named slots. This allows you to pass only HTML elements with the corresponding slot name so you can place different elements in as many places.
---
import Header from './Header.astro';
import Logo from './Logo.astro';
import Footer from './Footer.astro';
const { title } = Astro.props;
---
<div id="content-wrapper">
<Header />
<slot name="after-header" />
<Logo />
<h1>{title}</h1>
<slot />
<Footer />
<slot name="after-footer" />
</div>
To inject HTML content into a particular slot, use the slot attribute on any child element to specify the name of the slot, in this way:
---
import Wrapper from '../components/Wrapper.astro';
---
<Wrapper title="Fred's Page">
<img src="https://myphoto/john.jpg" slot="after-header" />
<h2>All about John</h2>
<p>Here is some stuff about John.</p>
<p slot="after-footer">Copyright 2024</p>
</Wrapper>
HTML Components, it's possible
Astro supports importing and using .html
files as components. You may want to use HTML components if you’re reusing code from an existing site built without a framework, or if you want to ensure that your component has no dynamic features.
HTML components must contain only valid HTML, and therefore lack key Astro component features:
- They don’t support frontmatter, server-side imports, or dynamic expressions.
- Any
<script>
tags are left unbundled, treated as if they hadis:inline
. - They can only reference assets that are in the
public/
folder.
Conclusion
Astro Components are fundamental for build your UI. Their encapsulation of functionality, reusability, and ease of integration make them a powerful tool for building scalable and maintainable website.
Happy coding!✨
Hi👋🏻
My name is Domenico, software developer passionate of Vue.js framework, I write article about it for share my knowledge and experience.
Don't forget to visit my Linktree to discover my projects 🫰🏻
Linktree: https://linktr.ee/domenicotenace
Follow me on dev.to for other articles 👇🏻
If you like my content or want to support my work on GitHub, you can support me with a very small donation.
I would be grateful 🥹