Astro: Manage Draft and Published Post Statuses

Astro does not provide a built-in draft property for content collections. This article adds a draft property to an Astro content collection schema and configures a filter to exclude unpublished entries from production builds while keeping them available during development.

AI-generated image of blog content management using draft and published posts.

Define Draft Property in Content Collection Schema

Start by updating the blog content collection schema in content.config.ts to add an optional draft boolean property.

draft: z.boolean().optional()

Each blog collection entry’s frontmatter can now use the draft property to mark the entry as either a draft (true) or published (false or not defined).

import { glob } from 'astro/loaders';
import { defineCollection, z } from 'astro:content';

const blog = defineCollection({
  loader: glob({ base: './src/content/blog', pattern: '**/*.{md,mdx}' }),
  schema: z.object({
    title: z.string(),
    pubDate: z.coerce.date(),
    draft: z.boolean().optional(),
  }),
});

export const collections = { blog };

Add Draft Property Filter

Add the following filter to getCollection() to exclude entries with the draft property set to true in the PROD environment.

.filter((post) => !import.meta.env.PROD || post.data.draft !== true)

For example, the blog collection is filtered to exclude drafts from production builds and sorted by published date in descending order.

const posts = (await getCollection('blog'))
  .filter((post) => !import.meta.env.PROD || post.data.draft !== true)
  .sort((a, b) => (b.data.pubDate?.valueOf() ?? 0) - (a.data.pubDate?.valueOf() ?? 0));

Apply the same filter to every getCollection() call within the project so draft entries remain excluded consistently in production builds, including blog indexes, tag pages, feeds, and related post lists.

Include Draft Status in Frontmatter

The draft property can now be added to the frontmatter of blog collection entries. If not set, the entry is assumed to be published and included in production builds.

---
title: 'Example Draft Post'
pubDate: 'Mar 10 2026'
draft: true
---

Results

In development (npm run dev), all blog entries are shown, regardless of draft status. After running a production build (npm run build), the preview server (npm run preview) serves the generated output. The filter excludes draft entries only where it is applied to getCollection().

Summary

Adding a draft property to a content collection schema and configuring a filter to exclude draft entries in production builds allows greater control over which blog entries are displayed in different environments. This approach ensures that draft content is excluded from production builds while remaining visible during development.