Astro’s Content Collections provide type-safe content management with automatic validation and TypeScript support.
What are Content Collections?
Content Collections are a way to organize and validate markdown/MDX content in Astro projects. They provide:
Type safety : Automatic TypeScript types for your content
Schema validation : Ensure all content has required fields
Better performance : Optimized content loading and querying
Developer experience : Autocomplete and type checking in your editor
Collection Configuration
The work collection is defined in src/content/work/config.ts:
src/content/work/config.ts
import { defineCollection , z } from 'astro:content' ;
export const collections = {
work: defineCollection ({
schema: z . object ({
title: z . string (),
description: z . string (),
publishDate: z . coerce . date (),
tags: z . array ( z . string ()),
img: z . string (),
img_alt: z . string (). optional (),
}),
}),
};
Schema Breakdown
Each field in the schema defines a validation rule using Zod :
title
Type : String
Required : Yes
Validation : Must be a non-empty string
Example : "VST Plugins"
description
Type : String
Required : Yes
Validation : Must be a non-empty string
Example : Multi-line project description
publishDate
publishDate : z . coerce . date ()
Type : Date
Required : Yes
Coercion : Automatically converts strings to Date objects
Format : YYYY-MM-DD HH:MM:SS or any valid date string
Example : 2024-03-10 00:00:00
z.coerce.date() automatically converts string dates from frontmatter into JavaScript Date objects, making it easy to sort and format dates.
tags : z . array ( z . string ())
Type : Array of strings
Required : Yes
Validation : Must be an array with at least one string element
Example : ["AI", "Web App", "React"]
img
Type : String
Required : Yes
Format : Path relative to public/ directory
Example : /assets/stock-2.jpg
img_alt
img_alt : z . string (). optional ()
Type : String
Required : No
Purpose : Accessibility - alternative text for images
Example : "AudioGPT application interface"
Querying Collections
Access content collections in your Astro components:
Get All Projects
import { getCollection } from 'astro:content' ;
const allProjects = await getCollection ( 'work' );
Get Sorted Projects
import { getCollection } from 'astro:content' ;
const projects = await getCollection ( 'work' );
const sortedProjects = projects . sort (
( a , b ) => b . data . publishDate . valueOf () - a . data . publishDate . valueOf ()
);
Filter Projects by Tag
import { getCollection } from 'astro:content' ;
const projects = await getCollection ( 'work' );
const webProjects = projects . filter ( project =>
project . data . tags . includes ( 'Web Development' )
);
Get Single Project
import { getEntry } from 'astro:content' ;
const project = await getEntry ( 'work' , 'audiogpt' );
Type Safety
Astro automatically generates TypeScript types for your collections:
import type { CollectionEntry } from 'astro:content' ;
// Type for a work collection entry
type WorkProject = CollectionEntry < 'work' >;
// Type for just the frontmatter data
type WorkData = CollectionEntry < 'work' >[ 'data' ];
// Use in component props
interface Props {
project : WorkProject ;
}
Example Component with Types
src/components/ProjectCard.astro
---
import type { CollectionEntry } from 'astro:content' ;
interface Props {
project : CollectionEntry < 'work' >;
}
const { project } = Astro . props ;
const { title , description , img , tags } = project . data ;
---
< article >
< img src = { img } alt = { project . data . img_alt } />
< h2 > { title } </ h2 >
< p > { description } </ p >
< ul >
{ tags . map ( tag => < li > { tag } </ li > ) }
</ ul >
</ article >
Validation Errors
If your content doesn’t match the schema, you’ll get helpful error messages:
Error: Invalid frontmatter for src/content/work/my-project.md:
- title: Required
- publishDate: Expected date, received string "invalid-date"
- tags: Expected array, received undefined
Extending the Schema
Add new fields to the schema as needed:
src/content/work/config.ts
import { defineCollection , z } from 'astro:content' ;
export const collections = {
work: defineCollection ({
schema: z . object ({
title: z . string (),
description: z . string (),
publishDate: z . coerce . date (),
tags: z . array ( z . string ()),
img: z . string (),
img_alt: z . string (). optional (),
// New fields
featured: z . boolean (). default ( false ),
githubUrl: z . string (). url (). optional (),
liveUrl: z . string (). url (). optional (),
status: z . enum ([ 'completed' , 'in-progress' , 'archived' ]). default ( 'completed' ),
}),
}),
};
Using Extended Schema
src/content/work/my-project.md
---
title : My Project
description : A great project
publishDate : 2024-03-15 00:00:00
tags : [ "Web Development" ]
img : /assets/project.jpg
featured : true
githubUrl : https://github.com/user/project
liveUrl : https://project.example.com
status : in-progress
---
Schema Validation Options
Optional Fields
field : z . string (). optional () // Can be undefined
Default Values
featured : z . boolean (). default ( false ) // Defaults to false if not provided
Enums
status : z . enum ([ 'draft' , 'published' , 'archived' ])
URLs
website : z . string (). url () // Validates URL format
Numbers
rating : z . number (). min ( 1 ). max ( 5 ) // Number between 1 and 5
Arrays with Validation
tags : z . array ( z . string ()). min ( 1 ). max ( 10 ) // 1-10 tags
Multiple Collections
You can define multiple collections:
import { defineCollection , z } from 'astro:content' ;
export const collections = {
work: defineCollection ({
schema: z . object ({
// work schema...
}),
}),
blog: defineCollection ({
schema: z . object ({
title: z . string (),
author: z . string (),
publishDate: z . coerce . date (),
content: z . string (),
}),
}),
};
Best Practices
Keep schemas strict Require essential fields and use optional() sparingly to maintain content quality.
Use coercion wisely z.coerce.date() is helpful for date strings, but validate other types explicitly.
Leverage TypeScript Use the generated types throughout your application for type safety.
Document your schema Add comments explaining the purpose and format of each field.
Check Content Validity
Run Astro’s content checking:
This validates all content against your schemas and checks for TypeScript errors.
Next Steps