Back to blog

Astro Content Collections: the blog setup I wish I had earlier

1 min read 0 views
astro typescript

Every static blog setup I built before Astro had the same failure mode: a typo in frontmatter that silently broke the page. A missing date, a tags string instead of an array, and the build would happily ship broken HTML.

Content Collections fix this with a schema.

Define the contract once

// src/content/config.ts
import { defineCollection, z } from 'astro:content';

const blog = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    description: z.string(),
    date: z.coerce.date(),
    tags: z.array(z.string()).default([]),
  }),
});

export const collections = { blog };

Now astro build fails with a precise error if any markdown file violates the schema. The date is a real Date object, not a string you have to parse in every template.

Querying is trivial

const posts = (await getCollection('blog')).sort(
  (a, b) => b.data.date.valueOf() - a.data.date.valueOf()
);

Fully typed. Your editor autocompletes post.data.title and yells at you for post.data.titel.

The part people miss

Reading time, tag pages, RSS feeds: all of it derives from the collection at build time. No client-side JavaScript, no runtime cost. The browser receives plain HTML, which is exactly what a blog should be.