Data, content, and assets
Query and render a collection
Load entries, filter drafts, sort dates, and pass one consistent content shape into the page.
Once a collection is defined, pages query it with getCollection() from astro:content. The function returns an array of entries, and each entry carries an id plus its frontmatter under data.
Here is a listing page:
---
import { getCollection } from "astro:content"
const notes = await getCollection("notes", ({ data }) => !data.draft)
notes.sort(
(a, b) => b.data.publishedAt.valueOf() - a.data.publishedAt.valueOf()
)
---
<ul>
{notes.map(note => (
<li><a href={`/notes/${note.id}/`}>{note.data.title}</a></li>
))}
</ul>
The second argument is a filter callback. Every entry it rejects disappears from the result, so drafts never produce a link here. Filter drafts before creating links or routes, not after, so unpublished URLs are not accidentally generated anywhere.
Do not rely on the loader’s entry order; it reflects how files were read, not any publishing policy. Sort explicitly where the listing policy belongs, as the publishedAt comparison above does.
For a detail route, select the entry in getStaticPaths() and render its body:
---
import { getCollection, render } from "astro:content"
export async function getStaticPaths() {
const notes = await getCollection("notes", ({ data }) => !data.draft)
return notes.map(note => ({ params: { id: note.id }, props: { note } }))
}
const { note } = Astro.props
const { Content } = await render(note)
---
<h1>{note.data.title}</h1>
<Content />
render() compiles the entry’s Markdown and hands back a <Content /> component you place in the template like any other component. Each generated page receives its own entry through props, so the detail route never re-queries the whole collection at render time.
Keep selection and sorting near the route. Presentational components should receive a small, consistent shape instead of knowing how the entire collection is stored.
A mistake worth recognizing: forgetting await in front of getCollection(). You get a Promise instead of an array, and the first symptom is usually notes.sort is not a function or an empty listing. Check the query line before suspecting the collection itself.
Build the site and count the generated note pages under dist/notes/. The number should match the entries that pass the draft filter, not the total files in the collection.
Lesson completed