Learning Astro - Content & Markdown
Episode 5 of 24

Learning Astro - Content & Markdown

This episode covers content management in Astro: writing with Markdown and MDX, content collections with validated schemas, frontmatter and metadata, and how to render dynamic content on static pages.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Astro is a framework for content-driven sites, and episode 5 sits right at its heart: how you write, manage, and display content. This is where Astro's power is felt the most — Markdown content becomes fast pages with strict validation.

You will learn three big things: writing content with Markdown and MDX, defining content collections with schemas validated at build time, and structuring frontmatter as metadata for every piece of content.

By the end of the episode, you can build a blog or documentation site with Astro — anyone writes the content, and the pages assemble automatically.

Using Markdown and MDX in Astro

Direct Markdown Pages in src/pages

.md files in src/pages automatically become pages. Create src/pages/blog/satu.md:

markdown
---
title: "Artikel Pertama"
description: "Contoh artikel markdown"
published: "2026-08-10"
---
 
## Pendahuluan
 
Ini adalah konten yang ditulis dengan **Markdown**. Astro merender
file ini menjadi halaman HTML statis saat build.
 
- Daftar item satu
- Daftar item dua

The frontmatter between --- becomes the page metadata, and the file body becomes the content. One thing to note: writing published: "2026-08-10" in the frontmatter is a metadata declaration — there is no validation at all until we define a collection.

MDX for Richer Content

If your content needs interactive components, use MDX. Enable the integration first:

Mengaktifkan MDX
npx astro add mdx

After that, .mdx files can use components directly inside the content. The key difference: Markdown is structured text only, while MDX allows JSX and components inside the content.

Content Collections with Astro Content

Defining a Collection with a Schema

Content collections turn Markdown from "a pile of files" into "validated data". Collection definitions live in src/content.config.ts (Astro 5) or src/content/config.ts (earlier versions):

JSsrc/content.config.ts
import { defineCollection, z } from "astro:content";
 
const blog = defineCollection({
  type: "content",
  schema: z.object({
    title: z.string(),
    description: z.string().optional(),
    published: z.date(),
    tags: z.array(z.string()),
  }),
});
 
export const collections = { blog };

The schema above uses z.object from Zod. At build time, Astro validates every frontmatter against the schema — if title is missing or published is not a date, the build fails with a clear message.

Placing Content in the Collection Directory

Content files for the blog collection live in src/content/blog. You can sort content with order() or mark drafts with .draft(). Complete metadata such as published and tags lets pages do filtering, sorting, and grouping.

Frontmatter and Page Metadata

Rendering Dynamic Content on Static Pages

To build a blog listing page, query the collection with getCollection:

JSsrc/pages/blog/index.astro
---
import { getCollection } from "astro:content";
 
const posts = (await getCollection("blog")).sort(
  (a, b) => b.data.published.valueOf() - a.data.published.valueOf(),
);
---
 
<h1>Daftar Artikel</h1>
<ul>
  {posts.map((post) => (
    <li>
      <a href={`/blog/${post.id}`}>
        {post.data.title}{post.data.published.toISOString().slice(0, 10)}
      </a>
    </li>
  ))}
</ul>

getCollection("blog") returns all valid entries, and each post.data holds metadata validated by the schema. This list is rendered into static HTML — visitors do not wait for any JavaScript.

Detail Pages with getStaticPaths

For per-slug article pages, combine the collection with getStaticPaths:

JSsrc/pages/blog/[slug].astro
---
import { getCollection } from "astro:content";
 
export async function getStaticPaths() {
  const posts = await getCollection("blog");
  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>

render(post) produces a ready-to-render content component, and the whole page is built once at build time.

Warning

Never drop Markdown files randomly into src/content. Only files matching a collection defined in content.config.ts will be processed — anything else triggers a build error.

Managing Metadata and Content Workflow

Consistency Through Schemas

The biggest benefit of content collections is consistency. Because every field is validated, contributors cannot forget to write description or use a date in the wrong format. For large amounts of content, use z.enum for categories and z.url for links so values are always valid.

Combining with Prefetch and Statistics

Because collection data is available as plain data structures, you can count articles per tag, build an index, or generate "related posts" lists without extra queries. All the computation runs at build time, so pages stay static and fast.

Conclusion

Episode 5 unlocks Astro's content management power: writing with Markdown and MDX, defining content collections with Zod schemas, using frontmatter as structured metadata, and rendering dynamic content on static pages through getCollection and render.

The key takeaways:

  • .md and .mdx files in src/pages automatically become pages.
  • Content collections validate frontmatter at build time.
  • Collection definitions live in src/content.config.ts.
  • getCollection("nama") fetches all valid entries.
  • getStaticPaths builds detail pages for every entry.
  • render(post) turns an entry into a content component.

In the next episode 6, we will cover data fetching and server-side rendering: fetching data at build time, integrating external APIs, SSR mode with adapters, and progressive and partial hydration. Your content will start connecting to the outside world.

Learning Astro - Content & Markdown | Learning Astro