I talked about how to work with Markdown files in a basic way.
If you build a content-heavy site, Astro has a powerful feature you will want to know about: content collections.
To work with collections, you create a new src/content
folder, and inside it, you create another folder with name of the type of content.
For example if you have blog posts you’ll have src/content/posts
. Or if you build a course on your site, you’ll have lessons, so you’ll have src/content/lessons
.
Here’s an example with blog posts:
src/content/posts/first.md
src/content/posts/second.md
src/content/posts/third.md
Inside src/content
you create a config.ts
file that will define the collection.
Here’s the most simple example of a collection:
import { defineCollection } from 'astro:content'
const collection = defineCollection({
type: 'content'
})
export const collections = {
posts: collection,
}
We can add more configuration to add required fields for the frontmatter of each markdown file, so if you miss for example the tag on a post, Astro will complain. But this is a start.
When we used markdown files before, we created them in the src/pages
folder, which automatically generated the route for us.
With content collections, we have to handle this.
We create a dynamic route under src/pages
. Remember how we made a dynamic route for some test data before?
Now we’ll serve content from the posts collection.
Say you want to have a blog in /blog
, and each post has the route /blog/<slug>
, like /blog/first
and /blog/second
Create a src/pages/blog/[slug].astro
Let’s replicate what we had made before with dynamic routes, we got the dynamic parameter from Astro.params
and we had a frontmatter with a getStaticPaths()
function exported:
---
import Layout from '../../layouts/Layout.astro'
const { slug } = Astro.params
export async function getStaticPaths() {
return [
//...
]
}
---
<Layout title=''>
</Layout>
Here’s how it works with collections:
---
import { getCollection } from 'astro:content'
import Layout from '../../layouts/Layout.astro'
const { slug } = Astro.params
export async function getStaticPaths() {
const posts = await getCollection('posts')
return posts.map(post => ({
params: { slug: post.slug }, props: { post },
}))
}
const { post } = Astro.props
const { Content } = await post.render()
---
<Layout title={post.data.title}>
<h1>{post.data.title}</h1>
<Content />
</Layout>
Now we also import getCollection
from Astro, and we use that to query for all the posts data using await getCollection('posts')
.
We use this data to populate the posts data returned from getStaticPaths()
.
The component then when asked to render a single item gets the post data from Astro.props
, and we use this to get a <Content />
component that’s responsible for displaying the content of the markdown file.
Finally, we return the markup.
Here’s the result:
http://localhost:4321/blog/first
http://localhost:4321/blog/second