Skip to main content

Content Collections in Astro

Series:Astro Complete GuidePart 3 of 3

Welcome to Part 3! In Part 1 and Part 2, we covered Astro basics and components. Now let’s master content management with Content Collections.

What are Content Collections?

Content Collections provide a type-safe way to manage your content (Markdown, MDX, JSON) with:

  • Type safety with Zod schemas
  • Auto-completion in your editor
  • Validation at build time
  • Powerful query API

Setting Up Collections

1. Define Your Schema

Create src/content.config.ts:

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

const articles = defineCollection({
  loader: glob({ pattern: '**/*.md', base: './src/content/articles' }),
  schema: z.object({
    title: z.string(),
    description: z.string().optional(),
    tags: z.array(z.string()).default([]),
    date: z.coerce.date().optional(),
    draft: z.boolean().default(false),
  }),
});

export const collections = { articles };
TypeScript

2. Add Content

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

---
title: "Hello World"
description: "My first post"
tags: [hello, blog]
date: 2025-01-01
---

# Hello World!

This is my first blog post.
Markdown

Querying Collections

Get All Entries

---
import { getCollection } from 'astro:content';

const posts = await getCollection('articles');
---

<ul>
  {posts.map(post => (
    <li>{post.data.title}</li>
  ))}
</ul>
Astro

Filter Entries

---
const publishedPosts = await getCollection('articles', ({ data }) => {
  return !data.draft;
});
---
Astro

Get Single Entry

---
import { getEntry } from 'astro:content';

const post = await getEntry('articles', 'hello-world');
---

<h1>{post.data.title}</h1>
Astro

Rendering Content

---
import { render } from 'astro:content';

const post = await getEntry('articles', 'hello-world');
const { Content } = await render(post);
---

<article>
  <h1>{post.data.title}</h1>
  <Content />
</article>
Astro

Dynamic Routes

Create src/pages/articles/[slug].astro:

---
import { getCollection, render } from 'astro:content';

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

const { post } = Astro.props;
const { Content } = await render(post);
---

<article>
  <h1>{post.data.title}</h1>
  <time>{post.data.date?.toLocaleDateString()}</time>
  <Content />
</article>
Astro

Advanced Schemas

Nested Objects

const articles = defineCollection({
  schema: z.object({
    title: z.string(),
    author: z.object({
      name: z.string(),
      email: z.string().email(),
    }),
  }),
});
TypeScript

Enums

schema: z.object({
  status: z.enum(['draft', 'published', 'archived']),
  lang: z.enum(['ko', 'en']).default('ko'),
})
TypeScript

References

schema: z.object({
  relatedPosts: z.array(z.string()),
})
TypeScript

Sorting and Filtering

Sort by Date

---
const posts = await getCollection('articles');
const sorted = posts.sort((a, b) => 
  b.data.date.getTime() - a.data.date.getTime()
);
---
Astro

Filter by Tag

---
const tag = 'astro';
const filtered = await getCollection('articles', ({ data }) => 
  data.tags.includes(tag)
);
---
Astro

Frontmatter Validation

Content Collections validate your frontmatter at build time:

---
title: "Valid Post"
date: 2025-01-01
tags: [astro]
---
Markdown

❌ This will fail:

---
title: 123  # Should be string!
date: "invalid-date"
---
Markdown

Working with MDX

import { defineCollection } from 'astro:content';

const articles = defineCollection({
  loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/articles' }),
  // ... schema
});
TypeScript

Now you can use components in your content:

---
title: "Interactive Post"
---

import Button from '../../components/Button.astro';

# Hello!

<Button>Click me</Button>
MDX

Best Practices

  1. Use TypeScript - Get full type safety
  2. Validate early - Define strict schemas
  3. Keep it organized - Use clear folder structure
  4. Use references - Link related content

Next Steps

You now know how to manage content in Astro! Check out the Astro docs for more advanced topics.

Summary

  • Content Collections provide type-safe content management
  • Define schemas with Zod
  • Query with getCollection() and getEntry()
  • Render with render()
  • Build dynamic routes with getStaticPaths()

This series covered the fundamentals of Astro. Now go build something amazing! 🚀

Related Posts


References

Comments