Astro Content collections Guide (For beginner 2023)
Managing contents in Astro (static site generator) is now more fun and safe with content collections
New Astro version astro@2.0.0
introducing “content collections” that will make our life easier to work with contents/collections on static site generated website.
Official doc for Astro Content collections
What is content collections in astro?
A content collection is a directory of files under src/content
folder that used as source for our “dynamic content”.
Dynamic content would be something like:
- blog post
- authors
- newsletter
- etc..
This collection will be organized nicely under src/content
folder. For example:
src/content
/blogs
/authors
In this sample we have two content collections, 1 for blog 1 for author
Sample visual how folder might be organize from astro official site
What files are allowed in content collections
Astro allows:
- md
- mdx
- yaml
- json
It’s recommended to use consistent naming scheme:
- lower case
- dashed instead of spaces
Why do you need astro content collections?
Astro content collections can help:
- organize your documents
- validate your frontmatter (no more typos!)
- Typescript (type safe checking) for all the content
After using the content collections for several Astro projects, I can see it helps me:
- separating “collections” from `pages“ folder. Which make it more clear where my “database” is. Yes! I think of this as my flat file database.
- Avoid typo in my frontmatter, as it do the type checking and throw an error if it doesn’t match our config setting.
How to start using Astro content collection?
Create a folder inside src/content
, use a relevant name to your collection. Classic sample is “blog”.
/src/content/blog/
Everytime you need to create a new blog post, you can start writing the file inside here with either .md or .mdx format.
/src/content/blog/first-post.md
Feel free to organize it by subdirectories as well.
Define collection config
Now to use the benefit of type and frontmatter checking at runtime, we need to create the config file.
Create config file
Create new file named src/content/config.ts
This is a special file that Astro will automatically load and use to configure your content collections.
Sample config file
Here is sample for our blog collection. Remember you can have more than one collection.
// 1. Import utilities from `astro:content`
import { defineCollection } from 'astro:content';
// 2. Define your collection(s)
// we'll learn defineCollection next
const blogCollection = defineCollection({ /* ... */ });
// 3. Export a single `collections` object to register your collection(s)
// This key should match collection dir name in "src/content"
export const collections = {
'blog': blogCollection,
};
Next we’ll learn what to put in your defineCollection
method
Defining a collection schema
To make our frontmatter consistent across the files, we’ll define the frontmatter structure here.
This code will be put in the defineCollection
method you’ve seen previously.
const blogCollection = defineCollection({
type: 'content', // Astro v2.5.0 and later
schema: z.object({
title: z.string(),
description: z.string(),
tags: z.array(z.string()),
image: z.string().optional(),
}),
});
Explanation
Inside schema, we’ll defined z object (Astro using zod) that match our frontmatter.
This assumes, that our fronmatter consist of (sample post.md file inside one of content collection folder)
title: "blog post title"
description: "this is a description"
tags: ["share", "code"]
image: "source.png"
- z.string() means a string data type
- .optional() means is not required , maybe some file have it, some don’t
- .array() means it consiste of array
You can learn more about possible data types in zod documentation
Get and display All the collections (query it!)
Astro is as flexible as javascript. You can query this “database” anywhere you want on your /pages folder.
Create a new file on your pages (you can name it whatever you want), just like other routing system.
Now in between the ---
---
import { getCollection, getEntry } from 'astro:content';
// Get all entries from a collection.
// Requires the name of the collection as an argument.
// Example: retrieve `src/content/blog/**`
const posts = await getCollection('blog');
// You can console with
console.log(posts)
---
Now you can loop this entries below the ---
wherever you want to display it.
The frontmatter data available at name.data
{
posts.map(post => {
return (
<a href={post.slug}>
<h2>{post.data.title}</h2>
<p>{post.data.description}</p>
</a>
)
})
}
Show single post Astro content collection
To retrieve single collection, you can use dynamic routing system in Astro. This sample is for building static output, so we use getStaticPaths
function.
For example below /pages/
folder, create new file /blog/[slug].astro
This assumes, you want your post displayed at name.com/blog/{post-slug}
Inside /blog/[slug].astro
---
import { CollectionEntry, getCollection } from 'astro:content';
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map((post) => ({
params: { slug: post.slug },
props: post,
}));
}
const post = Astro.props;
const { Content } = await post.render();
---
// Sample only
<h1> {post.data.title } </h1>
<div>
<Content />
</div>
Filter content collection in astro
You can do whatever you want with your content collections, including filter to your specific needs.
For example you want to display content based on the tags
// Example: Filter out content entries with `draft: true`
import { getCollection } from 'astro:content';
const publishedBlogEntries = await getCollection('blog', ({ data }) => {
return data.draft !== true;
});
Assume you have draft
in your frontmatter, and want to display non-draft only
Building for server output (SSR)
In case you don’t want to use “static output” in astro, we will use Astro.params
to get the slug
`
---
import { getEntry } from "astro:content";
// 1. Get the slug from the incoming server request
const { slug } = Astro.params;
if (slug === undefined) {
throw new Error("Slug is required");
}
// 2. Query for the entry directly using the request slug
const entry = await getEntry("blog", slug);
// 3. Redirect if the entry does not exist
if (entry === undefined) {
return Astro.redirect("/404");
}
// 4. (Optional) Render the entry to HTML in the template
const { Content } = await entry.render();
---
Frequently asked questions (FAQ)
How to exculde a file being built
If for any reason you want to exclude a file, you can start the filename with underscore (_). Reference: https://docs.astro.build/en/core-concepts/routing/#excluding-pages