Building My Website - Content management with Markdown and Frontmatter (Part 3)
- How I built my personal website - Part 3 - Implementing content collections in Astro using Markdown and schema validation
- 2 min read
This article is part of a series about building my personal website:
- The idea and choosing the right tool
- SEO essentials: meta tags and base head element
- Content management with Markdown and Frontmatter
- Semantic HTML for accessibility and external readers
- Minimalist CSS: styling and native-like design
- Adding color themes with JavaScript
- Astro plugins: RSS, Sitemap, Word count
- SVG icons and Favicon
- Building resume with XeLaTeX
Astro content collections
Astro’s content collections provide a powerful way to manage Markdown files with type-safe frontmatter validation. This system helps maintain consistency across all posts and enables advanced content organization.
Frontmatter schema validation
Each Markdown file in my collection includes the following frontmatter properties:
- title: The post’s title
- description: A brief summary for SEO and previews
- date: Publication date
- tags: Categorization labels
Here’s my schema definition in src/content.config.ts
:
import { glob } from "astro/loaders";
import { z, defineCollection } from "astro:content";
const posts = defineCollection({
loader: glob({ base: "./src/content/posts", pattern: "**/*.md" }),
schema: z.object({
title: z.string(),
description: z.string(),
date: z.date(),
tags: z.string().array(),
}),
});
export const collections = { posts };
Astro will automatically validate all Markdown files against this schema, providing error messages if any frontmatter is missing or invalid.
Markdown file example
A typical post includes the validated frontmatter followed by Markdown content:
---
title: Markdown and its features
description: About Markdown language and its capabilities
date: 2025-06-28
tags:
- review
---
# content
Retrieving and sorting posts
To fetch and display posts chronologically:
import { getCollection } from "astro:content";
const posts = (await getCollection("posts")).sort(
(a, b) => b.data.date.getTime() - a.data.date.getTime()
);
This sorted collection powers my blog index and tag pages.