技術 約2分で読めます

【Astro】ドラフト機能を実装する

正月用の記事を事前に書いておきたい。でもコミットしたら公開されてしまう。

ブランチを分ける? .gitignore に追加? どれも面倒。

シンプルに draft: true で制御できるようにする。

要件

  • draft: true の記事は本番ビルドで除外
  • 開発時は [DRAFT] プレフィックス付きで表示(誤公開防止)

実装

1. スキーマに draft フィールドを追加

const articles = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    // ...他のフィールド
    draft: z.boolean().optional(),
  }),
});

2. 記事取得のヘルパー関数

import { getCollection, type CollectionEntry } from 'astro:content'

type ArticleFilter = (entry: CollectionEntry<'articles'>) => boolean

export async function getPublishedArticles(
  filter?: ArticleFilter
): Promise<CollectionEntry<'articles'>[]> {
  return getCollection('articles', (entry) => {
    // 本番環境では draft 記事を除外
    if (import.meta.env.PROD && entry.data.draft) {
      return false
    }
    return filter ? filter(entry) : true
  })
}

import.meta.env.PROD で本番/開発を判定。開発時(pnpm dev)はドラフトも表示される。

3. 各ページで使用

---
// Before
import { getCollection } from 'astro:content';
const articles = await getCollection('articles', ({ data }) => data.category === 'diary');

// After
import { getPublishedArticles } from '@/lib/articles';
const articles = await getPublishedArticles(({ data }) => data.category === 'diary');
---

既存のフィルタ条件はそのまま引数で渡せる。

4. 開発時の視認性向上

ArticleCard と記事詳細ページで、開発時のみタイトルに [DRAFT] を付ける。

---
const { title, draft } = Astro.props;
const displayTitle = !import.meta.env.PROD && draft ? `[DRAFT] ${title}` : title;
---

<h2>{displayTitle}</h2>

使い方

frontmatterに draft: true を追加。

---
title: "まだ公開しない記事"
draft: true
---

公開時は draft: true を削除するだけ。

まとめ

  • import.meta.env.PROD で本番/開発を判定
  • ヘルパー関数で一元管理
  • 開発時のプレフィックス表示で誤公開を防止