Skip to content

Leverage Zod's power

Astro ^4.0.0

Zod is a TypeScript-first schema validation with static type inference. It’s used in many parts of Astro and can help you achieve more advanced behavior.

A common place where Astronauts want more customization is in the Content Collections config. We’ll look at 3 powerful Zod features that cover a few basic use-cases.

preprocess

People often want a bit more customization when referencing images inside their frontmatter.

Say if you’re using Path Aliases to reference your assets, and would like to automatically prepend an aliased asset path to your images:

src/content/blog/hello-world.md
---
title: Hello world
image: ~/src/assets/blog/hello-world.png
---

This can be easily be done using preprocess!

src/content/config.ts
import { defineCollection, z } from "astro:content"
export const collections = {
blog: defineCollection({
type: "content",
schema: ({ image }) => z.object({
title: z.string(),
image: image(),
image: z.preprocess(val => `~/src/assets/blog/${val}`, image()),
})
})
}

transform

You may not have the data in the shape you want in the frontmatter but you don’t want (or can’t) change all your entries. This is where transform comes in! This transform will allow us to you use those coordinates conveniently with library like mapbox-gl:

src/content/config.ts
import { defineCollection, z } from "astro:content"
export const collections = {
blog: defineCollection({
type: "content",
schema: z.object({
coordinates: z
.object({
latitude: z.number(),
longitude: z.number(),
})
.transform((e) => [e.longitude, e.latitude] as [number, number])
})
})
}

refine

You may want to add extra validation to your frontmatter. For example, what if you want to make sure your images are big enough? Let’s have a look at refine. You can set a condition that should be met, and a message that will be shown if not!

src/content/config.ts
import { defineCollection, z } from "astro:content"
export const collections = {
blog: defineCollection({
type: "content",
schema: ({ image }) => z.object({
image: image(),
image: image().refine((img) => img.width >= 1080, {
message: "Cover image must be at least 1080 pixels wide!",
})
})
})
}

Conclusion

We’ve only scratched the surface! Have a look at the official Zod docs to discover more powerful APIs!