Content collections are the best way to manage and author content in any Astro project. Collections help to organize your documents, validate your frontmatter, and provide automatic TypeScript type-safety for all of your content.
What are Content Collections?Section titled What are Content Collections?
A content collection is any top-level directory inside the reserved
src/content project directory, such as
src/content/authors. Only content collections are allowed inside the
src/content directory. This directory cannot be used for anything else.
A collection entry is any piece of content stored inside of your content collection directory. Entries can use content authoring formats including Markdown (
.md) and MDX (
.mdx using the MDX integration) or as data formats including YAML (
.yaml) and JSON (
.json). We recommend using a consistent naming scheme (lower-case, dashes instead of spaces) for your files to make it easier to find and organize your content, but this is not required.
Directorynewsletter/ the “newsletter” collection
- week-1.md a collection entry
- week-2.md a collection entry
- week-3.md a collection entry
Once you have a collection, you can start querying your content using Astro’s built-in content APIs.
The “.astro” DirectorySection titled The “.astro” Directory
Astro stores important metadata for content collections in an
.astro directory in your project. No action is needed on your part to maintain or update this directory. You are encouraged to ignore it entirely while working in your project.
.astro directory will be updated for you automatically anytime you run the
astro build commands. You can run
astro sync at any time to update the
.astro directory manually.
Organizing with multiple collectionsSection titled Organizing with multiple collections
If two files represent different kinds of content (e.g. a blog post and an author profile), they most likely belong in different collections. This is important because many features (frontmatter validation, automatic TypeScript type-safety) require that all entries in a collection share a similar structure.
If you find yourself working with different types of content, you should create multiple collections to represent each type. You can create as many different collections in your project as you’d like.
Organizing with subdirectoriesSection titled Organizing with subdirectories
A content collection is always a top-level folder inside of the
src/content/ directory. You cannot nest one collection inside of another. However, you can use subdirectories to organize your content within a collection.
For example, you can use the following directory structure to organize i18n translations within a single
docs collection. When you query this collection, you’ll be able to filter the result by language using the file path.
Directorydocs/ this collection uses subdirectories to organize by language
Defining CollectionsSection titled Defining Collections
To get the most out of your content collections, create a
src/content/config.ts file in your project (
.mjs extensions are also supported.) This is a special file that Astro will automatically load and use to configure your content collections.
Setting up TypeScriptSection titled Setting up TypeScript
If you do not already extend Astro’s
strictest recommended TypeScript settings in your
tsconfig.json file, you may need to update your
tsconfig.json to enable
If you use
.mjs files in an Astro project, you can enable IntelliSense and type checking in your editor by enabling
allowJs in your
Defining a collection schemaSection titled Defining a collection schema
Schemas enforce consistent frontmatter or entry data within a collection. A schema guarantees that this data exists in a predictable form when you need to reference or query it. If any file violates its collection schema, Astro will provide a helpful error to let you know.
Schemas also power Astro’s automatic TypeScript typings for your content. When you define a schema for your collection, Astro will automatically generate and apply a TypeScript interface to it. The result is full TypeScript support when you query your collection, including property autocompletion and type-checking.
To define your first collection, create a
src/content/config.ts file if one does not already exist (
.mjs extensions are also supported.) This file should:
- Import the proper utilities from
- Define each collection that you’d like to validate. This includes a
type(introduced in Astro v2.5.0) specifying whether the collection contains content authoring formats like Markdown (
type: 'content') or data formats like JSON or YAML (
type: 'data'). It also includes a
schemathat defines the shape of your frontmatter or entry data.
- Export a single
collectionsobject to register your collections.
Defining multiple collectionsSection titled Defining multiple collections
You can use
defineCollection() as many times as you want to create multiple schemas. All collections must be exported from inside the single
As your project grows, you are also free to reorganize your codebase and move logic out of the
src/content/config.ts file. Defining your schemas separately can be useful for reusing schemas across multiple collections and sharing schemas with other parts of your project.
Using third-party collection schemasSection titled Using third-party collection schemas
You can import collection schemas from anywhere, including external npm packages. This can be useful when working with themes and libraries that provide their own collection schemas for you to use.
Defining datatypes with ZodSection titled Defining datatypes with Zod
Astro uses Zod to power its content schemas. With Zod, Astro is able to validate every file’s frontmatter within a collection and provide automatic TypeScript types when you go to query content from inside your project.
To use Zod in Astro, import the
z utility from
"astro:content". This is a re-export of the Zod library, and it supports all of the features of Zod. See Zod’s README for complete documentation on how Zod works and what features are available.
Defining collection referencesSection titled Defining collection references
Collection entries can also “reference” other related entries.
reference() function from the Collections API, you can define a property in a collection schema as an entry from another collection. For example, you can require that every
space-shuttle entry includes a
pilot property which uses the
pilot collection’s own schema for type checking, autocomplete, and validation.
A common example is a blog post that references reusable author profiles stored as JSON, or related post URLs stored in the same collection:
This example blog post specifies the
slugs of related posts and the
id of the post author:
Defining custom slugsSection titled Defining custom slugs
type: 'content', every content entry generates a URL-friendly
slug property from its file
id. The slug is used to query the entry directly from your collection. It is also useful when creating new pages and URLs from your content.
You can override an entry’s generated slug by adding your own
slug property to the file frontmatter. This is similar to the “permalink” feature of other web frameworks.
"slug" is a special, reserved property name that is not allowed in your custom collection
schema and will not appear in your entry’s
Querying CollectionsSection titled Querying Collections
Astro provides two functions to query a collection and return one (or more) content entries:
Both functions return content entries as defined by the
Accessing referenced dataSection titled Accessing referenced data
Any references defined in your schema must be queried separately after first querying your collection entry. You can use the
getEntry() function again, or
getEntries(), to retrieve the referenced entry from the returned
Filtering collection queriesSection titled Filtering collection queries
getCollection() takes an optional “filter” callback that allows you to filter your query based on an entry’s
data (frontmatter) properties. For collections of
type: content, you can also filter based on
You can use this to filter by any content criteria you like. For example, you can filter by properties like
draft to prevent any draft blog posts from publishing to your blog:
The filter argument also supports filtering by nested directories within a collection. Since the
id includes the full nested path, you can filter by the start of each
id to only return items from a specific nested directory:
Using content in Astro templatesSection titled Using content in Astro templates
Once you have queried your collection entries, you can access each entry directly inside of your Astro component template. This lets you to render HTML for things like links to your content (using the content
slug) or information about your content (using the
For information about rendering your content to HTML, see Rendering Content to HTML below.
Passing content as propsSection titled Passing content as props
A component can also pass an entire content entry as a prop.
If you do this, you can use the
CollectionEntry utility to correctly type your components props using TypeScript. This utility takes a string argument that matches the name of your collection schema, and will inherit all of the properties of that collection’s schema.
Rendering content to HTMLSection titled Rendering content to HTML
Once queried, you can render Markdown and MDX entries to HTML using the entry
render() function property. Calling this function gives you access to rendered content and metadata, including both a
<Content /> component and a list of all rendered headings.
Generating Routes from ContentSection titled Generating Routes from Content
Content collections are stored outside of the
src/pages/ directory. This means that no routes are generated for your collection items by default. You will need to manually create a new dynamic route to generate HTML pages from your collection entries. Your dynamic route will map the incoming request param (ex:
src/pages/blog/[...slug].astro) to fetch the correct entry inside a collection.
The exact method for generating routes will depend on your build
output mode: ‘static’ (the default) or ‘server’ (for SSR).
Building for static output (default)Section titled Building for static output (default)
If you are building a static website (Astro’s default behavior), you would use the
getStaticPaths() function to create multiple pages from a single
src/pages/ component during your build.
getCollection() inside of
getStaticPaths() to query your content. Then, create your new URL paths using the
slug property of each content entry.
This will generate a new page for every entry in the
blog collection. For example, an entry at
src/content/blog/hello-world.md will have a slug of
hello-world, and therefore its final URL will be
Building for server output (SSR)Section titled Building for server output (SSR)
If you are building a dynamic website (using Astro’s SSR support), you are not expected to generate any paths ahead of time during the build. Instead, your page should examine the request (using
Astro.params) to find the
slug on-demand, and then fetch it using
Migrating from File-Based RoutingSection titled Migrating from File-Based Routing
This guide shows you how to convert an existing Astro project with Markdown files in the
src/pages/ folder to content collections. It uses the Build a Blog tutorial’s finished project as an example.
Upgrade to Astro v2.0 or later, and upgrade all integrations to their latest versions.
Set up TypeScript for content collections.
Create at least one collection (folder in
src/content/) and move your Markdown and MDX pages from
src/pages/into these subdirectories of
src/content/. Collections work best when all files in the same collection have similar frontmatter properties. So, choose your new folder structure to reflect similar types of pages.
For example, to migrate the blog posts in the tutorial, move the contents of
src/content/config.tsfile and define a schema for each content type. For the blog, we only have one content type,
Generate routes from your collections. Inside a collection, Markdown and MDX files no longer automatically become pages using Astro’s file-based routing, so you must generate the pages yourself.
For the tutorial, create a
src/pages/posts/[...slug].astro. This page will use dynamic routing and to generate a page for each collection entry.
This page will also need to query your collection to fetch page slugs and make the page content available to each route.
Render your post
<Content />within the layout for your Markdown or MDX pages. This allows you to specify a common layout for all of your posts.
layoutdefinition in each individual post’s frontmatter. Your content is now wrapped in a layout when rendered, and this property is no longer needed.
getCollection()to fetch content and metadata from your Markdown files. You will also need to update references to the returned post object, since you will now find your frontmatter values on the
The blog index page in the tutorial lists a card for each post. This becomes:
The tutorial blog project also dynamically generates a page for each tag. This page now becomes:
The same logic appears in the tag index page, which becomes:
Update the code that uses the publish date in the
pubDatewas a string. Now, after introducing types for your posts’ frontmatter,
Date. To render the date, convert it to a string:
Lastly, the tutorial blog project includes an RSS feed. This function must also use
dataobject, and be converted to an async function to do so:
For the full example of the blog tutorial using content collections, see the Content Collections branch of the tutorial repo.
Modifying Frontmatter with RemarkSection titled Modifying Frontmatter with Remark
Astro supports remark or rehype plugins that modify your frontmatter directly. You can access this modified frontmatter inside of a content entry by using the
remarkPluginFrontmatter property returned from
The remark and rehype pipelines only run when your content is rendered, which explains why
remarkPluginFrontmatter is only available after you call
render() on your content entry. In contrast,
getEntry() cannot return these values directly because they do not render your content.