Skip to content

Content & Data

Most Astro sites are content sites: a blog with dozens of posts, docs with hundreds of pages, a product catalog pulled from a CMS. The question this module answers is: how do you get that content — Markdown files, JSON, or a remote API — into your pages as typed, validated data you can query?

Astro’s answer is the Content Layer API: you define collections, each backed by a loader that reads content from somewhere (local files, a remote endpoint), and a schema that validates the shape. You then query collections with getCollection and getEntry, and render entry bodies with render. The result is content that behaves like a small typed database, generated at build time.

LessonWhat you’ll learn
Content collectionsThe Content Layer API: defineCollection, a loader, getCollection, and render
Schemas & referencesValidating frontmatter with Zod, and linking collections with reference()
Data fetchingTop-level await fetch(), build-time vs request-time data, headless CMS
Dynamic routesFile-based [slug].astro, getStaticPaths(), and pagination
flowchart LR
  files["Markdown / JSON / API"] --> loader["loader (glob / file)"]
  loader --> schema["schema validates (Zod)"]
  schema --> query["getCollection / getEntry"]
  query --> page["page renders HTML"]
From raw content to a rendered page

If you used Astro before version 5, the biggest shift is that collections are no longer magic folders. You used to drop Markdown in src/content/blog/ and it became a collection automatically. In Astro 6, every collection must declare a loader in src/content.config.ts. This is more explicit, but far more powerful: the same API now loads local files and remote data behind one typed interface.

What is the Content Layer API for?
What changed about collections in Astro 6 versus older versions?
Which two things does defineCollection combine?