ADR-027: Content Collections Schema Design

Documents the design decisions behind the five content collections (blog, projects, bio, experience, navigation), their schema fields, storage formats, and the rationale for each choice.

Last updated

Status

Accepted

Context

Astro’s Content Collections API provides type-safe, schema-validated content management. The starter ships with five collections pre-configured in src/content.config.ts. These represent the most common content types for portfolio and small production sites — the primary audience for this template.

The schema design decisions are non-obvious and affect how users extend the template. Without documentation, users frequently ask: why these collections, why these fields, why JSON for some and MDX for others.

Decision Drivers

  • Cover the common case: A portfolio/small-site user should be able to ship without defining their own schemas
  • Type safety: All fields must be validated with Zod at build time
  • Extensibility: Schemas should be easy to extend without breaking existing content
  • Format appropriateness: MDX for rich content, JSON for structured data
  • Draft support: All content collections support a draft field to hide unpublished content

Collections and Rationale

blog — MDX content

Purpose: Blog posts, articles, tutorials

Format: MDX (.mdx) — rich content with embedded components

Key schema decisions:

FieldTypeRationale
titlestringRequired — used in <title>, OG tags, listing pages
descriptionstringRequired — used in meta description and post cards
datedateRequired — enables chronological sorting
updateddate (optional)Shows “last updated” for evergreen content
authorstring (default: “Your Name”)Defaults to placeholder — replace with your name
tagsstring[] (default: [])Enables tag-based filtering
technologiesstring[] (default: [])Tech stack used in post
coverimage (optional)Astro image type — enables build-time optimisation
coverAltstringRequired for accessibility when cover image is used
cardImageimage (optional)Separate thumbnail for listing cards
featuredboolean (default: false)Pins post to featured placement
draftboolean (default: false)Excludes from production builds when true
readingTimenumber (optional)Can be calculated automatically
canonicalUrlurl (optional)For cross-posted content
relatedPostsstring[] (optional)Slugs of related posts

projects — MDX content

Purpose: Portfolio case studies, project showcases

Format: MDX — projects benefit from rich narrative content with images

Key schema decisions:

FieldTypeRationale
titlestringRequired
descriptionstring (max 160)Required — used in project cards and meta description
datedateRequired — enables chronological ordering
coverimageRequired — project hero image
coverAltstringRequired for accessibility
tagsstring[]Required — enables tag-based filtering
technologiesstring[]Required — tech stack used
cardImageimage (optional)Separate thumbnail for listing cards
featuredboolean (default: false)Pins project to featured placement
draftboolean (default: false)Excludes from production builds when true
clientstring (optional)Client name for case studies
durationstring (optional)Project duration (e.g. “3 months”)
rolestring (optional)Your role on the project
outcomes{metric, value, description?}[] (optional)Measurable results
externalUrlurl (optional)Live project or case study link
sortOrdernumber (default: 0)Manual sort override

bio — MDX content

Purpose: Author/about information — name, contact, social links, and biography prose

Format: MDX (.mdx) — supports narrative bio content alongside structured data fields

Key schema decisions:

FieldTypeRationale
namestringRequired
titlestringJob title / professional descriptor
locationstring (optional)City/region — no more specific for privacy
avatarimageAstro image type for optimisation
socialobject (optional)Typed social link map (github, linkedin, twitter, email)
skills{category, items[]}[] (optional)Skill groups for about page display

Why a collection not a config file? Collections are type-safe and queryable via getEntry(). A plain config file would require a separate import pattern and has no Zod validation.

experience — MDX content

Purpose: Work history / CV entries

Format: MDX (.mdx) — supports rich descriptions alongside structured fields

Key schema decisions:

FieldTypeRationale
titlestringRequired — job title
companystringRequired
locationstring (optional)Office location
startDatedateRequired — enables chronological sort
endDatedate (optional)Null = current role
currentboolean (default: false)Flag for current position
descriptionstringBrief summary of responsibilities
highlightsstring[] (optional)Bullet-point achievements
technologiesstring[] (optional)Tech stack used
ordernumber (default: 0)Manual sort override

Purpose: Header and footer navigation link lists

Format: JSON — pure structured data; no content, no MDX needed

Why a collection not hardcoded in components? Separating navigation from component code means non-developers can update nav links without touching .astro files. It also enables type-safe validation of href values.

Key schema decisions:

FieldTypeRationale
labelstringLink label
hrefstringURL path or external URL
isExternalboolean (default: false)Adds target="_blank" and rel attributes
iconstring (optional)Icon component name for navigation items
ordernumber (default: 0)Controls display order

Format Decision: MDX vs JSON

Use MDX when…Use JSON when…
Content has narrative proseContent is purely structured/tabular
Authors need to embed componentsData is queried programmatically
Content varies significantly between entriesAll entries share identical shape
Rich text formatting is neededSimple string/number/boolean fields only

Extending Schemas

To add a field to an existing collection:

  1. Add the Zod field to src/content.config.ts
  2. Make it optional with .optional() or provide a .default() to avoid breaking existing content files
  3. Run pnpm run check to validate
  4. Update existing content files if the field is required

To add a new collection:

  1. Define the schema in src/content.config.ts
  2. Create the directory src/content/<name>/
  3. Add at least one content file
  4. Run pnpm run check to confirm type generation

Consequences

Positive

  • Users can ship a portfolio site without writing any schema code
  • All content is type-safe — typos in frontmatter are caught at build time
  • JSON collections are easily edited by non-developers
  • Consistent draft pattern across all collections

Negative

  • Five pre-configured collections may feel opinionated for users with different content models
  • JSON collections cannot contain rich text — users needing narrative bio content must switch to MDX

Neutral

  • Schemas should be treated as the source of truth for content shape; component props should derive from collection types, not duplicate them

References


This ADR supersedes ADR-017 for the experience collection schema. ADR-017 provided the initial rationale for creating the collection; the schema details here are authoritative.


Date: 2026-02-18
Participants: Template maintainers
Outcome: Accepted