Install
$ agentstack add skill-incluud-astro-agent-skills-content-collection ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Astro Content Collections
Use this skill when creating or refactoring structured content in Astro, such as blogs, docs, changelogs, case studies, team data, or other schema-driven content.
If astro-best-practices is available, apply it alongside this skill for naming, accessibility, and performance defaults.
Workflow
1. Decide whether the collection is build-time or live
Most content sites should use build-time collections in src/content.config.*. Only reach for live collections in src/live.config.* when the data truly needs request-time freshness.
Before coding, decide:
- which collections exist
- which fields are required
- which relationships need references
- whether assets such as images should be validated
Favor a schema that reflects how the site queries content, not just how frontmatter currently looks.
2. Define collections in src/content.config.*
Use the current Content Layer API. For build-time collections:
- define them in
src/content.config.ts(or.js/.mjs) - give every collection a
loader - import
zfromastro/zod - do not use
type: 'content'ortype: 'data'
Example:
import { defineCollection, reference } from 'astro:content'
import { glob } from 'astro/loaders'
import { z } from 'astro/zod'
const blog = defineCollection({
loader: glob({ base: './src/content/blog', pattern: '**/*.{md,mdx}' }),
schema: ({ image }) => z.object({
title: z.string(),
description: z.string(),
publishDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
author: reference('authors'),
cover: image().optional(),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
}),
})
const authors = defineCollection({
loader: glob({ base: './src/data/authors', pattern: '**/*.json' }),
schema: z.object({
name: z.string(),
email: z.email().optional(),
avatar: z.url().optional(),
}),
})
export const collections = { blog, authors }
Useful patterns:
glob()for folders of local entriesfile()for a single JSON or other data filez.enum(...)for controlled valuesz.coerce.date()for frontmatter datesreference('collection-name')for relationshipsschema: ({ image }) => ...when image validation matters
3. Keep the collection shape current
For Astro 6 and newer:
- use
src/content.config.*, notsrc/content/config.* - use
entry.idas the slug-like identifier in URLs and queries - use
entry.filePathonly when you truly need the source path - use
getEntry()instead of legacygetEntryBySlug()orgetDataEntryById()
Prefer a stable folder structure and predictable IDs. Avoid scattering content across route folders if it is logically a collection.
4. Query with intent
Use the content APIs that match the job:
getCollection()for listsgetEntry()for a single known entrygetEntries()for arrays of references- collection filters for draft/published splits
Example:
---
import { getCollection } from 'astro:content'
const posts = await getCollection('blog', ({ data }) => !data.draft)
---
When rendering entries, keep route generation and content rendering separate enough that each part remains easy to reason about.
5. Wire routes and rendering with the current API
For dynamic routes:
- build paths from collection IDs
- pass the entry through props cleanly
- render content with
render(entry)
Example:
---
import { getCollection, render } from 'astro:content'
export async function getStaticPaths() {
const posts = await getCollection('blog')
return posts.map((post) => ({
params: { slug: post.id },
props: { post },
}))
}
const { post } = Astro.props
const { Content } = await render(post)
---
{post.data.title}
6. Sync and validate
After changing collections:
- run
npx astro syncor the repo’s normal Astro workflow - fix schema mismatches instead of weakening types
- validate at least one real entry per collection
- watch for warnings about missing loaders, legacy config paths, or deprecated imports
Migration Guidance
When migrating from older content patterns:
- move
src/content/config.tstosrc/content.config.ts - add a
loaderto every collection - remove any
type: 'content'ortype: 'data' - replace
import { defineCollection, z } from 'astro:content'withimport { defineCollection } from 'astro:content'andimport { z } from 'astro/zod' - replace
post.slugwithpost.id - replace
entry.render()withrender(entry) - replace
getEntryBySlug()andgetDataEntryById()withgetEntry()
If the project is crossing Astro versions at the same time, verify version-sensitive content APIs in the current official Astro docs before finalizing.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: incluud
- Source: incluud/astro-agent-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.