Documentation Index
Fetch the complete documentation index at: https://mintlify.com/withastro/docs/llms.txt
Use this file to discover all available pages before exploring further.
Content Collections are the best way to manage sets of similar content in Astro projects. They provide type safety, validation, automatic TypeScript typings, and powerful APIs for querying and rendering content.
What are Content Collections?
A collection is a set of data with a similar structure, such as blog posts, product listings, or author profiles. Collections can contain:
- Markdown, MDX, Markdoc files
- JSON, YAML, TOML files
- Remote data from a CMS or API
TypeScript Configuration
Content collections require specific TypeScript settings. Ensure your tsconfig.json includes:
{
"extends": "astro/tsconfigs/base",
"compilerOptions": {
"strictNullChecks": true,
"allowJs": true
}
}
Defining Collections
Create a src/content.config.ts file to define your collections:
import { defineCollection, z } from 'astro:content';
import { glob, file } from 'astro/loaders';
const blog = defineCollection({
loader: glob({ pattern: "**/*.md", base: "./src/data/blog" }),
schema: z.object({
title: z.string(),
description: z.string(),
pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
})
});
const authors = defineCollection({
loader: file("src/data/authors.json"),
schema: z.object({
name: z.string(),
portfolio: z.string().url(),
}),
});
export const collections = { blog, authors };
Built-in Loaders
glob() Loader
Loads files from directories using glob patterns:
const blog = defineCollection({
loader: glob({ pattern: "**/*.md", base: "./src/data/blog" }),
schema: z.object({
title: z.string(),
})
});
Supports multiple patterns:
loader: glob({
pattern: ['*.md', '!draft-*'],
base: 'src/data/posts'
})
file() Loader
Loads multiple entries from a single file:
const dogs = defineCollection({
loader: file("src/data/dogs.json"),
schema: z.object({
id: z.string(),
breed: z.string(),
}),
});
Use the parser option for custom file types:
import { parse as parseCsv } from "csv-parse/sync";
const cats = defineCollection({
loader: file("src/data/cats.csv", {
parser: (text) => parseCsv(text, { columns: true, skipEmptyLines: true })
})
});
Custom Loaders
Create inline loaders for remote data:
const countries = defineCollection({
loader: async () => {
const response = await fetch("https://restcountries.com/v3.1/all");
const data = await response.json();
return data.map((country) => ({
id: country.cca3,
...country,
}));
},
schema: z.object({
name: z.string(),
population: z.number(),
})
});
Schema Definition
Schemas enforce data validation using Zod:
const blog = defineCollection({
loader: glob({ pattern: "**/*.md", base: "./src/data/blog" }),
schema: z.object({
title: z.string(),
description: z.string(),
pubDate: z.coerce.date(),
tags: z.array(z.string()),
draft: z.boolean().default(false),
})
});
Collection References
Reference entries from other collections:
import { defineCollection, reference, z } from 'astro:content';
const blog = defineCollection({
loader: glob({ pattern: '**/[^_]*.md', base: "./src/data/blog" }),
schema: z.object({
title: z.string(),
author: reference('authors'),
relatedPosts: z.array(reference('blog')),
})
});
Querying Collections
Use helper functions to query your collections:
---
import { getCollection, getEntry } from 'astro:content';
// Get all entries
const allPosts = await getCollection('blog');
// Get a single entry
const post = await getEntry('blog', 'my-post');
---
Filtering Queries
Filter entries based on criteria:
const publishedPosts = await getCollection('blog', ({ data }) => {
return data.draft !== true;
});
Filter by environment:
const posts = await getCollection('blog', ({ data }) => {
return import.meta.env.PROD ? data.draft !== true : true;
});
Sorting Collections
Sort entries manually as needed:
---
const posts = (await getCollection('blog')).sort(
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf()
);
---
Rendering Content
Render Markdown and MDX content using the render() function:
---
import { getEntry, render } from 'astro:content';
const entry = await getEntry('blog', 'post-1');
const { Content, headings } = await render(entry);
---
<h1>{entry.data.title}</h1>
<p>Published: {entry.data.pubDate.toDateString()}</p>
<Content />
Generating Routes
Static Sites
Use getStaticPaths() to generate pages:
---
import { getCollection, render } from 'astro:content';
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map(post => ({
params: { id: post.id },
props: { post },
}));
}
const { post } = Astro.props;
const { Content } = await render(post);
---
<h1>{post.data.title}</h1>
<Content />
SSR Sites
Fetch entries on demand:
---
import { getEntry, render } from "astro:content";
const { id } = Astro.params;
if (!id) return Astro.redirect("/404");
const post = await getEntry("blog", id);
if (!post) return Astro.redirect("/404");
const { Content } = await render(post);
---
<h1>{post.data.title}</h1>
<Content />
Type Safety
Use CollectionEntry type for components:
---
import type { CollectionEntry } from 'astro:content';
interface Props {
post: CollectionEntry<'blog'>;
}
const { post } = Astro.props;
---
<article>
<h2>{post.data.title}</h2>
<p>{post.data.description}</p>
</article>
Accessing Referenced Data
Query referenced entries separately:
---
import { getEntry, getEntries } from 'astro:content';
const blogPost = await getEntry('blog', 'welcome');
const author = await getEntry(blogPost.data.author);
const relatedPosts = await getEntries(blogPost.data.relatedPosts);
---
<h1>{blogPost.data.title}</h1>
<p>Author: {author.data.name}</p>
<h2>Related Posts:</h2>
{relatedPosts.map(post => (
<a href={post.id}>{post.data.title}</a>
))}
JSON Schemas
Astro generates JSON Schema files for collections in .astro/collections/. Use them in your editor for IntelliSense:
{
"$schema": "../../../.astro/collections/authors.schema.json",
"name": "Jane Doe",
"skills": ["Astro", "TypeScript"]
}
When to Use Collections
Use content collections when you:
- Have multiple files sharing the same structure
- Need type safety and validation
- Want optimized querying for thousands of entries
- Need to fetch remote content from a CMS
Don’t use collections when you:
- Have only one or a few unique pages
- Are displaying unprocessed static files
- Need real-time data updates