Content & Data
The content problem
Section titled “The content problem”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.
What this module covers
Section titled “What this module covers”| Lesson | What you’ll learn |
|---|---|
| Content collections | The Content Layer API: defineCollection, a loader, getCollection, and render |
| Schemas & references | Validating frontmatter with Zod, and linking collections with reference() |
| Data fetching | Top-level await fetch(), build-time vs request-time data, headless CMS |
| Dynamic routes | File-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"]
The one big change to know
Section titled “The one big change to know”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.