Build a fast Astro blog in 20 minutes
A no-nonsense walkthrough of standing up a content-first blog with Astro — content collections, layouts, and a production build that flies.
Astro has quietly become the default answer for content-heavy sites, and once you’ve shipped one it’s easy to see why: you write mostly HTML and Markdown, you ship almost no JavaScript, and the thing is fast without you fighting for it. Here’s the shortest path from empty folder to a working blog.
1. Scaffold the project
Start from the minimal template so nothing is in your way:
npm create astro@latest my-blog -- --template minimal --typescript strict
cd my-blog
You get a tiny project: a config file, a src/pages directory, and not much
else. That emptiness is the point — you add only what you need.
2. Define a content collection
This is the piece that makes Astro click. Instead of loose Markdown files, you describe the shape of your content with a Zod schema and get type safety plus validation for free:
// src/content.config.ts
import { glob } from 'astro/loaders';
import { defineCollection, z } from 'astro:content';
const posts = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content/posts' }),
schema: z.object({
title: z.string(),
pubDate: z.coerce.date(),
draft: z.boolean().default(false),
}),
});
export const collections = { posts };
Now every post is validated at build time. Forget a title? The build fails
loudly instead of shipping a broken page.
3. Render a post
A dynamic route pulls an entry and renders it. The render() call hands you a
Content component that outputs the Markdown body:
---
import { getCollection, render } from 'astro:content';
export async function getStaticPaths() {
const posts = await getCollection('posts');
return posts.map((post) => ({ params: { slug: post.id }, props: { post } }));
}
const { post } = Astro.props;
const { Content } = await render(post);
---
<article>
<h1>{post.data.title}</h1>
<Content />
</article>
4. Build and ship
npm run build
You get a dist/ folder of static HTML you can drop on any host — no server, no
runtime, no cold starts. The whole site is pre-rendered.
Where to go next
From here the additions are small and composable: an RSS feed via @astrojs/rss,
a sitemap integration, Tailwind through the Vite plugin, and pagination using
Astro’s built-in paginate() helper. Each one is a few lines, and none of them
slow the site down. That restraint — adding capability without adding weight —
is the whole reason Astro is worth learning.


