komal.nakrani
Browse Themes
ThemesCategoriesDocsBlogTips & Tricks
Content & MDX••By Komal Nakrani

Astro Content Collections API Cheatsheet

Astro content collections cheatsheet: defineCollection, glob() and file() loaders, Zod schemas, getCollection, getEntry, render, reference and live collections.

#Astro#Cheatsheet#Content Collections

This Astro content collections cheatsheet puts the whole API on one page for Astro 7: where the config lives, the two built-in loaders, schema patterns, every query function, rendering, references and live collections. For explanations and worked examples, the Astro content collections guide goes deeper; this page is for quick lookups.

Key Takeaways

  • Config: src/content.config.ts, exporting collections.
  • Loaders: glob() for folders, file() for one JSON/YAML file, or your own function.
  • Queries: getCollection(), getEntry(), getEntries(); render with render(entry).
  • Import z from astro/zod.

Imports

import { defineCollection, reference } from 'astro:content';        // config
import { glob, file } from 'astro/loaders';                          // config
import { z } from 'astro/zod';                                       // config
import { getCollection, getEntry, getEntries, render } from 'astro:content'; // pages
import type { CollectionEntry } from 'astro:content';                // types

Define a collection

// src/content.config.ts
const blog = defineCollection({
  loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: './src/content/blog' }),
  schema: ({ image }) => z.object({
    title: z.string(),
    pubDate: z.coerce.date(),
    cover: image().optional(),
    tags: z.array(z.string()).default([]),
    draft: z.boolean().default(false),
    author: reference('authors'),
  }),
});

const authors = defineCollection({
  loader: file('src/data/authors.json'),
  schema: z.object({ name: z.string() }),
});

export const collections = { blog, authors };

Loaders

Loader Entries Id comes from
glob({ pattern, base }) One per matching file File path without extension, or slug frontmatter
glob({ ..., generateId }) One per file Your function
file('data.json') One per array item or object key The item’s id or the key
async () => [...] One per returned object The object’s id
Object loader { name, load } Whatever load stores store.set({ id, data })

Schema patterns

z.string().max(70)                       // length limit
z.coerce.date()                          // "2026-10-28" -> Date
z.enum(['draft', 'live'])                // fixed values
z.array(z.string()).default([])          // missing -> []
z.boolean().default(false)               // missing -> false
z.url().optional()                       // valid URL or missing
z.number().min(1).max(5)                 // range
image()                                  // local image, validated and optimisable
reference('authors')                     // id in another collection
z.array(reference('blog'))               // list of ids

Query

const all = await getCollection('blog');
const live = await getCollection('blog', ({ data }) => !data.draft);
const sorted = live.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());

const one = await getEntry('blog', 'first-post');        // or undefined
const author = await getEntry(post.data.author);          // resolve a reference
const related = await getEntries(post.data.related);      // resolve many

getCollection() doesn’t guarantee order. Always sort.

Entry shape

Property What it is
entry.id Unique id within the collection
entry.collection Collection name
entry.data Validated frontmatter or data
entry.body Raw Markdown body (file-based entries)
entry.filePath Source file path (file-based entries)
entry.rendered Pre-rendered HTML metadata, when available

Render

---
const { Content, headings, remarkPluginFrontmatter } = await render(post);
---
<Content />
<Content components={{ h2: MyHeading }} />   <!-- MDX element mapping -->

headings powers a table of contents; remarkPluginFrontmatter carries values like reading time.

Static routes from a collection

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

Use [...slug].astro if ids contain slashes.

Types

type Post = CollectionEntry<'blog'>;
function card(post: Post) { return post.data.title; }

Live collections

// src/live.config.ts
import { defineLiveCollection } from 'astro:content';

Query with getLiveCollection() and getLiveEntry() on on-demand pages. They return { entries | entry, error } instead of throwing. Use them only for data that must be fresh per request.

CLI

npx astro sync    # regenerate collection types

Errors you’ll meet

Message Fix
Frontmatter does not match schema Fix the field or schema
Collection is missing a loader Add glob() or file()
Legacy content config file found Move to src/content.config.ts
Render an undefined entry Guard getEntry() results
Duplicate content entry slug Make ids unique

The content docs for Komal themes show a theme’s collections, and the astro:content reference has full signatures.

FAQ

Where do content files have to live?

Anywhere. glob()’s base option points at the folder; src/content/ is only a convention.

Can one collection mix Markdown and JSON?

No. A collection is either file-per-entry Markdown/MDX (or data files) via glob(), or entries from file(). Use two collections and a reference() between them.