Free Astro tutorials for complete beginners.

Beginner's Guide to Astro

Lesson 5 of 7, about 25 minutes

Write blog posts in Markdown

Set up a content collection so that adding a blog post is as simple as adding a text file.

Tested on Astro 7.4

Markdown is a simple way to write formatted text. Astro can turn a folder of Markdown files into a blog. This is called a content collection.

Write your first post

Create src/content/posts/hello-world.md:

---
title: Hello, world
date: 2026-11-06
---

This is my **first** blog post, written in Markdown.

The section at the top holds facts about the post. The rest is the post itself.

Describe your collection

Create src/content.config.ts. Note that it goes directly inside src, not inside content.

import { defineCollection } from 'astro:content';
import { glob } from 'astro/loaders';
import { z } from 'astro/zod';

const posts = defineCollection({
  loader: glob({ base: './src/content/posts', pattern: '**/*.md' }),
  schema: z.object({
    title: z.string(),
    date: z.coerce.date(),
  }),
});

export const collections = { posts };

The schema is a checklist. If a post has no title, Astro tells you which file is wrong instead of building a broken page.

List your posts

Create src/pages/blog/index.astro:

---
import { getCollection } from 'astro:content';
import Layout from '../../layouts/Layout.astro';

const posts = (await getCollection('posts')).sort(
  (a, b) => b.data.date.valueOf() - a.data.date.valueOf(),
);
---
<Layout title="Blog">
  <h1>Blog</h1>
  <ul>
    {posts.map((post) => (
      <li><a href={`/blog/${post.id}/`}>{post.data.title}</a></li>
    ))}
  </ul>
</Layout>

Make a page for each post

Create src/pages/blog/[id].astro. Yes, the square brackets are part of the file name. They tell Astro this one file makes many pages.

---
import { getCollection, render } from 'astro:content';
import Layout from '../../layouts/Layout.astro';

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

const { post } = Astro.props;
const { Content } = await render(post);
---
<Layout title={post.data.title}>
  <h1>{post.data.title}</h1>
  <p>{post.data.date.toDateString()}</p>
  <Content />
</Layout>

Stop your dev server with Ctrl and C, then run npm run dev again so Astro picks up the new collection. Click the Blog link and open your post.

What you’ve done

To publish a new post, you now add one Markdown file. That’s all. Try adding a second one.

Try "blog", "deploy" or "EmDash".