Astro Components

By

An introduction to Astro components, the .astro files that pair an HTML template with a build-time frontmatter script, plus scoped CSS, imports, and MDX.

~~~

When you create an Astro project, you’ll see some files ending with the .astro extension.

Let’s take a look at one.

Let’s pick the one shipped in the Minimal template:

---
---

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" type="image/x-icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width" />
    <title>Welcome to Astro</title>
  </head>

  <body>
    <h1>Welcome to <a href="https://astro.build/">Astro</a></h1>
  </body>
</html>

The component is basically HTML, except there are two --- lines at the top. That’s the frontmatter. You might be familiar with this concept from Markdown files, for example I use it in this post in Hugo to set the page title, and the post date.

Note that you could as well omit the frontmatter if it’s empty, and just start the component with an HTML tag. In this case it’s there because this is the default Astro example.

But the interesting thing in Astro is that it can contain JavaScript (or TypeScript if you prefer).

Note that the first example above contained the html, head and body tags because that was a page component, a special kind of component that’s responsible of responding to a route (and lives in src/pages).

Inside src/pages you can also put .md files (and .mdx files, once you add the @astrojs/mdx integration), and they will be considered pages. Astro will render them as plain HTML unless you set a layout. We’ll see what this means in another post.

Components can be much simpler, like you see for example in this demonstration of how to define variables in the frontmatter using JavaScript (or TypeScript) and we use them in the HTML in a JSX-like syntax:

---
const name = 'Flavio'
---

<p>{name}</p>

This JavaScript code runs at build time, not in the browser. If you want to add JavaScript that’s ran in the browser, you can add a script tag in the page:

---
const name = 'Flavio'
---

<p>{name}</p>
<script>
  alert('test')
</script>

And you can do much more than just defining variables.

You can use the frontmatter to import components or libraries. You can fetch data. You can define variables that will then be available in the HTML.

In any component you can define scoped CSS using the style tag:

---
const name = 'Flavio'
---

<p>{name}</p>
<style>
  p {
    color: red;
  }
</style>

When you define a component in the src/components folder, it’s then available anywhere in your Astro components, you just need to import it and you embed it:

---
import Test from '../components/Test.astro'
---

<Test />

You can pass data into that child with Astro props the same way.

It’s not really JSX, but it’s actually an improvement.

For example, to modify the head part of a page component, just add a head tag. You can comment using normal HTML comments, <!-- --> instead of {/* */}. You can use HTML special characters. And HTML attributes don’t need to be camelCased. No more className=.

Small things, but yeah. It makes it simpler.

When I wrote this post, Astro shipped a built-in <Markdown> component you could use to embed some Markdown inside a component. Astro 1.0 moved it out of core into @astrojs/markdown-component, a legacy package that doesn’t work with server rendering and hasn’t had a release since 2023. If you want Markdown mixed with components and expressions today, use MDX (.mdx files, with the @astrojs/mdx integration). Plain Markdown goes in .md files under src/pages or in a content collection, and you render it from a layout.

Astro still ships a few built-in components. The two that come from astro:components are Code and Debug.

Code renders a syntax-highlighted block at build time, using Shiki:

---
import { Code } from 'astro:components'
---

<Code code={`const name = 'Flavio'`} lang="js" />

If you prefer Prism (the library this blog used when I wrote this post), install @astrojs/prism and import the Prism component from that package.

Debug prints the value of a frontmatter variable right into the page, so you can inspect build-time data in the browser:

---
import { Debug } from 'astro:components'
const name = 'Flavio'
---

<Debug {name} />

Older snippets import it with import Debug from 'astro/debug'. That path still resolves in Astro 7, but the docs only show astro:components, so use that in new code. And since the frontmatter runs at build time, a plain console.log() in there works too, the output shows up in the terminal where astro dev is running.

Tagged: Astro · All topics

Want me to talk about your product? You can sponsor this site.

~~~

Related posts about astro: