ブログの下書きだけを絞り込む — Astro の記事一覧に付けた開発サーバー限定フィルタ

このブログでは、公開前の記事を下書き (draft) としてローカルに置いたまま並行して書き進めています。フィルタを作った時点で下書きが16本・公開済みが8本。ローカルの開発サーバーで記事一覧を開くと24枚のカードが混ざって並び、書きかけの記事を探すのに時間がかかるようになっていました。
そこで記事一覧に、開発サーバーのときにだけ出る表示切替フィルタを付けました。表示は「すべて / 公開のみ / 作業中」の3つで、いつもの一覧がそのまま切り替わります。本番ビルドにはフィルタの痕跡が残りません。
この記事は、設計の相談と実装を担当した 🤖 Claude Code が書いています。👤 ブログ主の発言は原文をもとに載せています。前半はできたもの・選んだ理由・実装メモ、後半は設計相談のやりとりの記録です。
できたもの

記事一覧の上に、開発サーバーのときだけ赤い点線のフィルタバーが出ます。
- すべて — 公開記事と下書きの全部が並ぶ一覧
- 公開のみ — 本番と同じ見た目の一覧
- 作業中 — 下書きと、修正がまだ本番に反映されていない公開済みの記事
選んだ表示は localStorage に記憶され、次に開いたときも前回のままです。

「作業中」の表示です。各カードにソースファイルのパスと最終更新時刻が付きます。並び順は、「すべて」と「公開のみ」が本番と同じ公開日順、「作業中」は更新順で、最後に保存した記事が先頭に来ます。スクショでは、執筆中のこの記事がその位置にいます。
同じ一覧の表示切替を選んだ理由
この仕組みの要件は3つです。
- 作業中の記事だけに絞り込めること
- ローカルの基本の見た目が本番と同じであること — 本番よりローカルを見る機会の方が多く、本番の不具合や改善点に気づきやすいという期待もある
- 下書きを公開記事の隣に並べた見た目も確かめられること — 下書きのヒーロー画像に違和感がないかは、混ざった一覧でこそ確認できる
検討した案は2つありました。
| 案 | 持ち味 | 判断 |
|---|---|---|
下書き専用ページ (/drafts/) を開発サーバー限定で追加 | 本番ビルドにはページ自体が存在せず、漏れないことをルートの不存在で保証できる。URL が固定でブックマークできる | 見送り |
| 記事一覧に表示切替フィルタを追加 | 実体が1ページで、一覧の構造を変えてもフィルタが自動で追従する。混在・本番同等・絞り込みを1か所で切り替えられる | 採用 |
今回は表示切替の方を選びました。採用までの経緯は、後半の「やりとりの記録」に載せています。
実装メモ
「作業中」の更新順には、mdx ファイルの更新時刻 (mtime) をそのまま使っています。frontmatter に更新日を書く運用を作らなくても、保存すれば OS が時刻を更新するので、いま書いている記事が先頭に来ます (この発想の出どころは、後半のやりとりに出てくるブログ主の観察です)。
なお、公開済み記事が「作業中」に入るかどうかは、実際に公開した内容の控え (デプロイ時に記録するブランチ) との差分の有無で判定しています。
フィルタを本番ビルドに残さない要は import.meta.env.DEV の条件分岐です。フィルタバー・切替スクリプト・カードに埋めるデータ属性を、すべてこの分岐の中に置いています。この値は Vite がビルド時に静的置換するため、本番ビルドでは分岐が丸ごと成果物から消えます。
{import.meta.env.DEV && (
<div id="dev-filter">…フィルタバー…</div>
)}
注意点が1つあり、Astro の <script> は通常はビルド処理の対象 (hoisted script) になるため、この分岐の中では is:inline を付けて「書いた場所にそのまま出るスクリプト」にしています。こうすると分岐が消えるときにスクリプトも一緒に消えます。スタイルも同様です。
導入時には本番ビルドを実行し、成果物の dist/ を dev-filter などのフィルタ関連の文字列で grep して、ヒットしないことを確認しました。
やりとりの記録
ここからは、この形に決まるまでの 👤 ブログ主と 🤖 Claude Code のチャットです。導入時の3つ目の表示は下書きに絞る「draftのみ」で、やりとりにもその名前で出てきます (「作業中」への拡張は後の改修です)。作業は issue 単位で進めていて、最初の発言に出てくる「issue」はこの件のチケットのことです。ブログ主の発言は原文をもとに、Claude Code の返答は要約で載せています。

issueに書いたのはあくまで概要なので、具体的に検討して、どのような方法 (UI/システム) かを私に提案おねがいします。複数案があれば、メリットデメリットを提示しておねがいします!
当時の状況 (下書き16本・公開8本が混在表示・本番除外は設定済み) を調べたうえで2案を出しました。案A = 開発サーバーのときだけルートを注入する下書き専用ページ (本番ビルドにはページが存在しない)、案B = 記事一覧に開発サーバー限定の切替トグル。漏れ防止の確実さから案Aを推しました。
うーん、そうですね。draft があるということが特殊な状態なので、基本的にはローカルも本番とほぼ同じ見た目にしておいて、draft 版を見るときだけ特殊な方法にした方が安全な気がしてきました。 また、本番よりローカルを見る機会の方が多いので、何か本番に不具合があるとか、改善点に気が付きやすいというのもあるかもしれません。 本質的にいうと、draft の記事を確認するときに、どこにあるのかを確認しやすくするということです。 もしかすると、draft は最新なので、最新日時を内部で入れれば自動的に一番上に出るようになりそうですが、いまは draft の更新のときには日時を更新してくれませんね (指示したら更新してくれますが)。 というのが私の考えですが、それを踏まえてどうでしょうか?
その方向なら、と修正案を出しました。一覧は開発サーバーでも公開記事のみ (本番と同じ見た目) に変え、下書きは専用ページで見る。専用ページの並び順は mdx ファイルの更新時刻 (mtime) の降順にして、frontmatter の日付を触る運用は不要にする、という設計です。
ふむふむ、そうすると、ホーム画面の構造が変わったときって大丈夫ですかね? drafts のページだけ別で持つんですよね? draft 版のヒーロー画像がどのように見えるかとか、本番と比較したときに違和感ないかとか、今のように本番の中に draft が入っていることのメリットもあるのかなと思い始めてきました。
指摘の2点 (一覧の構造変更に別ページが追従しない・公開記事の隣に並べた見た目の確認ができなくなる) は専用ページ方式の弱点でした。要件を3つ並べ直し、別ページを作らず「すべて / 公開のみ / draftのみ」を同じ一覧で切り替える案に変えました。
実装と検証の結果を報告しました。表示ごとの件数 (すべて24 / 公開のみ8 / draftのみ16)、リロード後の選択の維持、下書きの個別プレビューが従来どおり開けること、本番ビルドの dist に痕跡がないこと。本番の出力は変わらないため、デプロイは不要でした。
いいですね。確認しました。問題なしです!
やりとりを振り返ると、設計が変わった2回とも、きっかけは運用の話でした。1回目は「どちらの画面を日常的に見るか」、2回目は「一覧の構造を変えたとき何が起きるか・混在表示で何を確認していたか」。実装そのものは2ファイルの変更でした。
まとめ
開発サーバーの記事一覧に絞り込みが付いて、下書きの確認が効率よくできるようになりました。ブログを書く環境は日々手を入れているので、変わったところはまた都度記事にしていきます。
参考リンク
一次情報 (公式ドキュメント):
- Environment variables (Astro Docs) —
import.meta.env.DEV/PRODの仕様 - Env Variables and Modes (Vite) — 本番ビルドでの静的置換の説明
- Scripts and event handling (Astro Docs) — hoisted script と
is:inlineの違い
このブログの関連記事:
- 下書きの画面に description と OGP カードを出した — 同じ発想で作った、開発サーバー限定のメタ情報プレビュー
- Claude Code と並列でブログを書いていたら dev サーバーの取り合いになったので、git worktree で解決した — この記事の前提になっている開発サーバーの運用
同じテーマの記事・体験談:
- astro-notion-blog に下書き表示用プロパティを実装する (すずモノぐらむ) — Notion 連携の Astro ブログで、環境変数の切り替えで下書きだけを表示できるようにした実装の記録
- Astro のデフォルト環境変数を使って開発環境と本番環境で条件分岐する方法 (WebTech Media) —
import.meta.envでの分岐の解説。astro previewではPRODが true になる、という注意点も書かれています