Acumatica · Astro

Astro Content Collections

Astro content collections turn a folder of Markdown into type-safe, schema-validated content you query with confidence — the right way to structure a content-driven Astro site.

John Kihiu12 min read

Astro's content collections API gives you a type-safe way to organise and query local Markdown, MDX, and data files without reaching for a full CMS. Instead of scattering front-matter conventions across a folder of loose files and hoping they stay consistent, a collection is defined by a schema, validated at build time, and queried through a typed API — errors in a post's front matter surface as build failures instead of silent runtime bugs.

Defining a collection and its schema

Collections live under src/content/, and each collection's shape is declared in src/content/config.ts using Zod schemas. A blog collection might require a title, publish date, and optional tags array; Astro validates every entry in the collection against that schema when you run astro build or astro dev, so a missing required field or a wrong type fails immediately rather than producing a broken page at request time.

TS · CONTENT CONFIG
import { defineCollection, z } from 'astro:content';

const blog = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    pubDate: z.coerce.date(),
    tags: z.array(z.string()).default([]),
    draft: z.boolean().default(false),
  }),
});

export const collections = { blog };

Querying with getCollection and getEntry

Once a collection is defined, getCollection('blog') returns every validated entry with full TypeScript autocomplete on the front matter fields, and getEntry('blog', slug) fetches a single entry. Filtering happens in plain JavaScript — getCollection('blog', ({ data }) => !data.draft) excludes drafts from a production build without a separate query language. This is the piece that replaces a lot of ad-hoc glob-and-parse logic teams used to write against raw Markdown files.

The Content Layer API and remote sources

Since Astro 4.14, the Content Layer API extends collections beyond local files: a loader can pull entries from a remote API, a headless CMS, or a database, and the results are cached and validated the same way as local Markdown. This is the piece that matters if content genuinely lives outside the repo — you get the same typed query surface (getCollection, getEntry) regardless of where the data originated, instead of writing a bespoke fetch-and-cache layer per source.

Loaders decouple the schema from the source

A custom loader implements a load() function that populates the collection's data store; the schema validation and query API stay identical whether the loader reads local files or calls a remote endpoint. This makes swapping a CMS backend later a loader change, not a template rewrite.

Generating routes from a collection

Static routes are generated the standard Astro way: a dynamic route file (src/pages/blog/[slug].astro) calls getStaticPaths, which maps over getCollection('blog') and returns a params/props pair per entry. Each entry's render() function returns the compiled content plus a table-of-contents-friendly heading list, so the page component gets rendered Markdown/MDX output without a separate render call.

Schema changes are breaking changes

Tightening a schema — making an optional field required, or narrowing a type — will fail the build on any existing entry that doesn't conform. Treat schema changes on a collection with real content the same as a database migration: check what's already there before you ship the change.

When to reach for a headless CMS instead

Content collections are the right fit when content is authored by developers or technical writers comfortable with Markdown and Git-based review. Once non-technical editors need a UI, scheduled publishing, or asset management beyond what a file tree gives you, pairing a headless CMS with a Content Layer loader is usually less friction than building that tooling around raw Markdown files yourself.

Use caseFit
Developer-authored blog/docsLocal Markdown collection, schema-validated
Marketing site with non-technical editorsContent Layer loader + headless CMS
Data-driven pages (product catalog, etc.)Content Layer loader against an API/database

Wrapping up

Content collections turn what used to be an informal folder-of-Markdown convention into something the build actually checks. Start with local collections and a strict schema for anything developer-authored, and reach for the Content Layer API's loader system only once content genuinely needs to live outside the repository.

John Kihiu
Acumatica ERP Developer · Laravel Engineer

Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.