Content collections
Added in:
astro@2.0.0
Content collections are the best way to manage sets of content in any Astro project. Collections help to organize and query your documents, enable Intellisense and type checking in your editor, and provide automatic TypeScript type-safety for all of your content. Astro v5.0 introduced the Content Layer API for defining and querying content collections. This performant, scalable API provides built-in content loaders for your local collections. For remote content, you can use third-party and community-built loaders or create your own custom loader and pull in your data from any source.
Projects may continue using the legacy Content Collections API introduced in Astro v2.0. However, we encourage you to update any existing collections when you are able.
What are Content Collections?
Section titled What are Content Collections?You can define a collection from a set of data that is structurally similar. This can be a directory of blog posts, a JSON file of product items, or any data that represents multiple items of the same shape.
Collections stored locally in your project or on your filesystem can have entries of Markdown, MDX, Markdoc, or JSON files:
Directorysrc/
- …
Directorynewsletter/ the “newsletter” collection
- week-1.md a collection entry
- week-2.md a collection entry
- week-3.md a collection entry
Directoryauthors/ the “author” collection
- authors.json a single file containing all collection entries
With an appropriate collection loader, you can fetch remote data from any external source, such as a CMS, database, or headless payment system.
TypeScript configuration for collections
Section titled TypeScript configuration for collectionsContent collections rely on TypeScript to provide Zod validation, Intellisense and type checking in your editor. If you are not extending one of Astro’s strict
or strictest
TypeScript settings, you will need to ensure the following compilerOptions
are set in your tsconfig.json
:
Defining Collections
Section titled Defining CollectionsIndividual collections use defineCollection()
to configure:
- a
loader
for a data source (required) - a
schema
for type safety (optional, but highly recommended!)
The collection config file
Section titled The collection config fileTo define collections, you must create a src/content.config.ts
file in your project (.js
and .mjs
extensions are also supported.) This is a special file that Astro will use to configure your content collections based on the following structure:
Defining the collection loader
Section titled Defining the collection loaderThe Content Layer API allows you to fetch your content (whether stored locally in your project or remotely) and uses a loader
property to retrieve your data.
Built-in loaders
Section titled Built-in loadersAstro provides two built-in loader functions (glob()
and file()
) for fetching your local content, as well as access to the API to construct your own loader and fetch remote data.
The glob()
loader creates entries from directories of Markdown, MDX, Markdoc, or JSON files from anywhere on the filesystem. It accepts a pattern
of entry files to match, and a base
file path of where your files are located. Each entry’s id
will be automatically generated from its file name. Use this loader when you have one file per entry.
The file()
loader creates multiple entries from a single local file. Each entry in the file must have a unique id
key property. It accepts a base
file path to your file and optionally a parser
function for data files it cannot parse automatically. Use this loader when your data file can be parsed as an array of objects.
parser
function
Section titled parser functionThe file()
loader accepts a second argument that defines a parser
function. This allows you to specify a custom parser (e.g. toml.parse
or csv-parse
) to create a collection from a file’s contents.
The file()
loader will automatically detect and parse a single array of objects from JSON and YAML files (based on their file extension) with no need for a parser
unless you have a nested JSON document. To use other files, such as .toml
and .csv
, you will need a to create a parser function.
The following example defines a content collection dogs
using a .toml
file:
After importing TOML’s parser, you can load the dogs
collection into your project by passing both a file path and parser
function to the file()
loader. A similar process can be used to define a cats
collection from a .csv
file:
Nested .json
documents
Section titled Nested .json documentsThe parser
argument also allows you to load a single collection from a nested JSON document. For example, this JSON file contains multiple collections:
You can seperate these collections by passing a custom parser
to the file()
loader for each collection:
Building a custom loader
Section titled Building a custom loaderYou can build a custom loader to fetch remote content from any data source, such as a CMS, a database, or an API endpoint.
Using a loader to fetch your data will automatically create a collection from your remote data. This gives you all the benefits of local collections, such as collection-specific API helpers such as getCollection()
and render()
to query and display your data, as well as schema validation.
Find community-built and third-party loaders in the Astro integrations directory.
Inline loaders
Section titled Inline loadersYou can define a loader inline, inside your collection, as an async function that returns an array of entries.
This is useful for loaders that don’t need to manually control how the data is loaded and stored. Whenever the loader is called, it will clear the store and reload all the entries.
The returned entries are stored in the collection and can be queried using the getCollection()
and getEntry()
functions.
Loader objects
Section titled Loader objectsFor more control over the loading process, you can use the Content Loader API to create a loader object. For example, with access to the load
method directly, you can create a loader that allows entries to be updated incrementally or clears the store only when necessary.
Similar to creating an Astro integration or Vite plugin, you can distribute your loader as an NPM package that others can use in their projects.
Defining the collection schema
Section titled Defining the collection schemaSchemas enforce consistent frontmatter or entry data within a collection through Zod validation. 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.
Every frontmatter or data property of your collection entries must be defined using a Zod data type:
Defining datatypes with Zod
Section titled Defining datatypes with ZodAstro uses Zod to power its content schemas. With Zod, Astro is able to validate every file’s data 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.
Zod schema methods
Section titled Zod schema methodsAll Zod schema methods (e.g. .parse()
, .transform()
) are available, with some limitations. Notably, performing custom validation checks on images using image().refine()
is unsupported.
Defining collection references
Section titled Defining collection referencesCollection entries can also “reference” other related entries.
With the 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 id
s of related posts and the id
of the post author:
Defining custom IDs
Section titled Defining custom IDsWhen using the glob()
loader with Markdown, MDX, Markdoc, or JSON files, every content entry id
is automatically generated in an URL-friendly format based on the content filename. The id
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 id
by adding your own slug
property to the file frontmatter or data object for JSON files. This is similar to the “permalink” feature of other web frameworks.
Querying Collections
Section titled Querying CollectionsAstro provides helper functions to query a collection and return one (or more) content entries.
getCollection()
fetches an entire collection and returns an array of entries.getEntry()
fetches a single entry from a collection.
These return entries with a unique id
, a data
object with all defined properties, and will also return a body
containing the raw, uncompiled body of a Markdown, MDX, or Markdoc document.
CollectionEntry
type.
Using content in Astro templates
Section titled Using content in Astro templatesAfter querying your collections, you can access each entry’s content directly inside of your Astro component template. For example, you can create a list of links to your blog posts, displaying information from your entry’s frontmatter using the data
property.
Rendering body content
Section titled Rendering body contentOnce queried, you can render Markdown and MDX entries to HTML using the render()
function property. Calling this function gives you access to rendered HTML content, including both a <Content />
component and a list of all rendered headings.
Passing content as props
Section titled Passing content as propsA component can also pass an entire collection entry as a prop.
You can use the CollectionEntry
utility to correctly type your component’s 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.
Filtering collection queries
Section titled Filtering collection queriesgetCollection()
takes an optional “filter” callback that allows you to filter your query based on an entry’s id
or data
properties.
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:
You can also create draft pages that are available when running the dev server, but not built in production:
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:
Accessing referenced data
Section titled Accessing referenced dataAny references defined in your schema must be queried separately after first querying your collection entry. You can use the getEntry()
function to return a single referenced item, or getEntries()
to retrieve multiple referenced entries from the returned data
object.
Generating Routes from Content
Section titled Generating Routes from ContentContent collections are stored outside of the src/pages/
directory. This means that no pages or routes are generated for your collection items by default.
You will need to manually create a new dynamic route if you want to generate HTML pages for each of your collection entries, such as individual blog posts. Your dynamic route will map the incoming request param (e.g. Astro.params.slug
in src/pages/blog/[...slug].astro
) to fetch the correct entry for each page.
The exact method for generating routes will depend on whether your pages are prerendered (default) or rendered on demand by a server.
Building for static output (default)
Section titled Building for static output (default)If you are building a static website (Astro’s default behavior), use the getStaticPaths()
function to create multiple pages from a single page component (e.g. src/pages/[slug]
) during your build.
Call getCollection()
inside of getStaticPaths()
to have your collection data available for building static routes. Then, create the individual URL paths using the id
property of each content entry. Each page is passed the entire collection entry as a prop for use in your page template.
This will generate a page route for every entry in the blog
collection. For example, an entry at src/blog/hello-world.md
will have an id
of hello-world
, and therefore its final URL will be /posts/hello-world/
.
If your custom slugs contain the /
character to produce URLs with multiple path segments, you must use a rest parameter (e.g. [...slug]
) in the .astro
filename for this dynamic routing page.
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.request
or Astro.params
) to find the slug
on-demand, and then fetch it using getEntry()
.
Explore the src/pages/
folder of the blog tutorial demo code on GitHub to see full examples of creating pages from your collections for blog features like a list of blog posts, tags pages, and more!
When to create a collection
Section titled When to create a collectionYou can create a collection any time you have a group of related data or content that shares a common structure.
Much of the benefit of using collections comes from:
- Defining a common data shape to validate that an individual entry is “correct” or “complete”, avoiding errors in production.
- Content-focused APIs designed to make querying intuitive (e.g.
getCollection()
instead ofimport.meta.glob()
) when importing and rendering content on your pages. - A Content Loader API for retrieving your content that provides both built-in loaders and access to the low-level API. There are several third-party and community-built loaders available, and you can build your own custom loader to fetch data from anywhere.
- Performance and scalability. The Content Layer API allows data to be cached between builds and is suitable for tens of thousands of content entries.
Define your data as a collection when:
- You have multiple files or data to organize that share the same overall structure (e.g. blog posts written in Markdown which all have the same frontmatter properties).
- You have existing content stored remotely, such as in a CMS, and want to take advantage of the collections helper functions and Content Layer API instead of using
fetch()
or SDKs. - You need to fetch (tens of) thousands of related pieces of data, and need a querying and caching method that handles at scale.
When not to create a collection
Section titled When not to create a collectionCollections provide excellent structure, safety, and organization when you have multiple pieces of content that must share the same properties.
Collections may not be your solution if:
- You have only one or a small number of different pages. Consider making individual page components such as
src/pages/about.astro
with your content directly instead. - Your data is not stored in a supported format, such as TOML. Use other methods of importing files or fetching data instead.
- You are displaying files that are not processed by Astro, such as PDFs. Place these static assets in the
public/
directory of your project instead. - You are using APIs that need to be updated in real time. Content collections are only updated at build time, so if you need live data you should consider on-demand rendering.