·
astro typescript content
· Updated February 5, 2025

Content Collections in Practice

A practical guide to structuring and querying content with Astro's type-safe collections.

Content collections are one of Astro’s most powerful features. They bring type safety to your markdown and MDX content, catching errors at build time rather than runtime.

Defining your schema

Start by creating a content.config.ts file in your project root:

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

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

export const collections = { blog }

Querying content

With your schema defined, querying content becomes type-safe:

import { getCollection } from 'astro:content'

// All published posts, sorted by date
const posts = await getCollection('blog', (post) => !post.data.draft)
const sorted = posts.sort((a, b) => 
  b.data.publishedAt.getTime() - a.data.publishedAt.getTime()
)

Notice how TypeScript knows the shape of post.data automatically. Try to access a non-existent field, and you’ll get an immediate error.

Dynamic routes

Generate pages from your collection using dynamic routes:

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

Each post gets its own statically-generated page at build time.

Rendering content

To render MDX content, use the render function:

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

<Content />

The rendered content preserves all your MDX components and styling.

Validation benefits

The real power comes from validation. If you forget a required field in your frontmatter:

---
title: My Post
description: Missing publishedAt!
---

Astro will fail the build with a clear error message, preventing broken pages from reaching production.

Next steps

Content collections work well with:

  • Sitemap generation via @astrojs/sitemap
  • Draft workflow with conditional filtering
  • Custom transformers for content processing

Experiment with these patterns in your own projects.