Skip to content

Astro

Astro is a static site generator that outputs plain HTML. It has its own component syntax (.astro files) that looks like HTML with a frontmatter block.


---
// This runs at BUILD TIME only - not in the browser
const title = "Hello World";
const posts = await getCollection('blog');
---
<!-- This is the HTML template -->
<h1>{title}</h1>
{posts.map(p => <a href={p.slug}>{p.data.title}</a>)}

The --- fences are the frontmatter. Everything inside runs as TypeScript/JavaScript at build time. The HTML below is the output. No JavaScript ships to the browser from this file unless you explicitly add a <script> tag.


Every .astro page in src/pages/ becomes a static HTML file. index.astro -> index.html. about.astro -> about/index.html.

Do not add output: "server" or output: "hybrid" to astro.config.mjs. Leaving it out defaults to "static". This is what we want.

ComponentName.astro
---
interface Props {
title: string;
highlight?: boolean;
}
const { title, highlight = false } = Astro.props;
---
<div class:list={["card", { "card--highlight": highlight }]}>
<h2>{title}</h2>
<slot /> <!-- children go here -->
</div>

Layouts are just components with a <slot />. Pages wrap themselves in a layout:

---
import Layout from "../layouts/Layout.astro";
---
<Layout title="About us">
<h1>About</h1>
</Layout>

src/pages/blog/[slug].astro generates one page per blog post. Requires getStaticPaths():

---
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map(p => ({ params: { slug: p.slug }, props: { post: p } }));
}
const { post } = Astro.props;
---

Blog posts live in src/content/blog/ as Markdown files. Define the schema in src/content.config.ts:

import { defineCollection, z } from 'astro:content';
const blog = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
description: z.string(),
pubDate: z.date(),
tags: z.array(z.string()).default([]),
}),
});
export const collections = { blog };

Fetch posts anywhere:

import { getCollection } from 'astro:content';
const posts = await getCollection('blog');

API Use
Astro.url.pathname Current page path - used for canonical URL
Astro.props Props passed to this component
getCollection('blog') Fetch all blog posts
<slot /> Where children render in a layout/component
<Fragment> Render multiple elements without a wrapper
class:list={[]} Conditional classes
set:html={string} Render raw HTML (used for JSON-LD)
is:inline on <script> Script runs as-is in the browser, not bundled

We use Astro 7. Check package.json for the exact version. Astro releases frequently - always check the migration guide before upgrading across major versions.