Astro × Headless CMS で技術ブログを作る
AstroとSveltiaCMSで作るブログサイト
もともとはAstro + microCMSで技術ブログを作っていたが、マークダウンのサポートが手薄く、エディタの使用感も好みではなかった。
- 記事をマークダウンで書きたい
- 書いた記事はgitで管理したい(変更差分の追跡や、作業したことをGithubの草に反映したい)
と思い、Sveltia CMS上でマークダウンで記事を書き、マークダウンファイルを生成してAstro Content Collectionでマークダウンファイルを管理することにした。
その過程で記事が生成されるディレクトリのサブモジュール化や、AstroとDecap/Sveltia CMS連携などを行ったのであたりの知見をメモとしてまとめる。
使用技術の変遷
使用する技術を変えるたびに、思うところが出てきてそのたびに更新してきたので、その一連の流れをざっくりと書いていく。
1. Astro + microCMS
一番最初にブログサイトを作ってみようと思い立った時にAstroを学び始めていたこともあり、ヘッドレスCMSと統合して記事の管理はCMS、UIはAstroで作ろうという思いヘッドレスCMSを探していた。 日本発のヘッドレスCMSということで何となくmicroCMSを選定して実際に繋ぎこんでみた。 APIをカスタマイズすることができて、そのAPIをもとにAstroからコンテンツを取得できるのはわかりやすさや拡張性の面でいいなと思いつつ、このCMSはWordPress同様独自のリッチテキストエディタが採用されていて、マークダウンで記述するには非公式プラグインを入れるなどをしないといけなかったり、細かいバグ(エディタに文字を入力できない時があるなど)があったりしたので別のヘッドレスCMSへの移管を考え始めた。
変更理由
- マークダウンでの記述は実現はできるが、APIレスポンスに不満はある(HTMLパースが必要になったり)
- エディタの細かいバグが気になる
2. Astro Content Collection + hygen
使い慣れた形式でもあり、ZennやNotionで書いた記事をコピペで崩れることなく移行できるのも利便性がいいためマークダウンで記事を書きたかった。
AstroはContent Collectionという、マークダウンファイルの内容をパースしてAstroファイルで使用できるようにするという機能があるのでそれを使えばいいかと思い、Astroプロジェクト内でマークダウンを追加して記事を書き、その内容を表示させることにした。
Astroプロジェクト内でマークダウンファイルを管理するので、記事がgit管理できるという点もよかった。
ただ、ヘッドレスCMSなどと連携しなければVSCodeなどのエディタで記事を書かなければいけなく、毎回マークダウンを手動で作成してフロントマターもコピペして…という作業が大変だったので、テンプレートくらいは作成したいなと思い、hygenを採用。(さらに言うと後述するObsidianでの記述もこの時点で検討した)
CAUTION
hygen 公式サイトに飛ぶと危険なサイトへリダイレクトされるのでアクセスしないように注意 以下のGithubリポジトリであれば問題ないが、“hygen.io” にはアクセスしないように
変更理由
- マークダウン形式で書ける上に、git管理もできてやりたいことは実現できたが、フロントマターで管理する記事のカテゴリの入力が手動で面倒だった
例えば以下のように、フロントマターに定義したカテゴリをUIに出力しているのだが、これはただのマークダウンファイルなので定義済みのカテゴリを表示するサジェスト機能などもなく、カテゴリは手作業で追加する必要があった。zodを使って定数管理しているカテゴリのみを受け付けるようにしているので、定義されていないカテゴリを使おうとしたら実行時エラーは出るのだが、手動での入力なためタイポもしてしまうことも少なくなかった。
---title: Astroについてslug: astro-content-collectionpublishedAt: 2025-11-16thumbnail: /images/thumbnail/noimage.webpgithubUrl: ''categories: - Astrostatus: published---
## Astroについて
Astroは、ブログやマーケティング、eコマースなど、コンテンツ駆動のウェブサイトを作成するためのウェブフレームワークです。Astroは...- ローカルだけじゃなくてマルチデバイス(異なるPC間、スマートフォンなど)で記事を書けるようにしたくなった(git pullを各デバイスで行うのはとても面倒なのでリアルタイムな同期が理想)。
3. Decap CMS(旧: NetlifyCMS)
Git ベースの軽量ヘッドレス CMSという立ち位置。
普通、ヘッドレスCMSでもCMSでもDBがあるものが多いが、これは記事の作成はMarldownファイルで行われ、リモートリポジトリにcommitされる仕組みを持ってる。
なので記事情報はすべてMarkdownファイルにあり、DBが不要。
Astroと相性がすごくいい。
移行してから感じたのは、管理画面でカテゴリがサジェストで表示されるようになったのはかなりうれしかった。 元NetlifyCMSだったらしく、その名の通りNetlifyにホスティングしないとダメそうだったので、AmplifyにホスティングしていたものをNetlifyへ移管。

変更理由
- UI/UXがそこそこ悪い
- 記事プレビューやスマートフォン表示時のUIの質が特に悪い(レスポンシブ×)
4. Sveltia CMS
これが現在使っているもの。 DecapCMSの互換性があって、とにかく移管が楽(OAuth周りは少し調整必要かも)。 Decapの全欠点を解消。モバイル対応・UI/UXともに大幅によくなった。

ここまでで、
- 記事はすべてマークダウンで書ける
- Git管理が可能
- マルチデバイスで記事を書くことができてリアルタイムのデータの同期が可能
- ヘッドレスCMSはエディタと比べて、メタデータの管理がしやすい
といったようないい体験を求めて使用する技術を変えてきたけど、もう一つ解決したい不満があった。
記事の編集履歴をプライベートに閉じる
記事をGit管理することで、記事の更新差分を見ることができたり、記事を更新したときにGithubにその作業履歴が残り草に反映させることができた。
ただ、ただ単に記事をgit管理すると下書きレベルの拙い文章や、publicにしたくない重要な記述(技術ブログというのもあり、ソースコードの添付などをするときに誤ってシークレットキーなどが含まれていたり)などがあると思うので、
Astroプロジェクト(ブログ本体)自体はpublicにして、マークダウンファイルが集約されるディレクトリ(src/content/{posts_name})のみをprivateリポジトリにしたく、このディレクトリをsubmoduleとして切り出すことにした。
そうすることで、マークダウンファイルの変更履歴はきちんと積み重なり、自分だけがその差分を確認できるという状況を作ることができる。
サブモジュール運用
- 親リポジトリ:astro-markdown-blog(Astro本体+UI・CMS連携・公開用)
- サブモジュール:blog-posts(Markdown記事+imagesを全てここに集約、private運用)
astro-markdown-blog/ # 親リポジトリ(Astro)└── src/ └── content/ └── posts/ # サブモジュール(記事) ├── images/ ├── foo.md └── bar.md構成は上記になったが、いくつかの問題はあった。
サーバー側でsubmoduleの初期化ができない
サーバー側でデプロイ前に
git submodule update --init --recursiveを実行して、.gitmodulesの内容をもとにサブモジュールの初期化を行うのだが、その際にprivateリポジトリなサブモジュールを取得できずデプロイが始まらなかった。
privateなリポジトリにアクセスするための必要情報が足りず、エラーでデプロイが止まっていた。
[submodule "src/content/posts"] path = src/content/posts url = git@github.com:k-ito-cat/blog-posts.git branch = mainやり方の詳細は調べたらすぐ出てくるので省くが、GithubでDeploy keyを発行してNetlifyに公開鍵を登録するだけ。
NOTE
Deploy Key を使う場合、.gitmodulesのGithub URLは、SSH接続をするときにDeploy Keyを参照するため SSH URL を使うようにしておく必要がある。
SveltiaCMSで記事作成後、Netlifyのデプロイが走らない
SveltiaCMSで記事作成・更新などをした後、サブモジュールのリポジトリに直接pushされるのでサブモジュールは更新されるけど、ブログ側のリポジトリが更新されないので変更が検知されず、Netlifyのデプロイが走らなかった。
結論、Github actionsでいろいろやって無理やり解決させた。 流れは
CMSで保存時にサブモジュール(blog-posts)にcommit/pushが走る ↓サブモジュールのGitHub Actionsでスーパープロジェクト(親リポジトリ)にイベント通知 ↓スーパープロジェクトのGitHub Actionsで、サブモジュールからイベントの通知を受け取った後、 submodule update → commit & push(スーパープロジェクトでサブモジュールの変更差分を取り入れる必要があるのでそれをしている。ポインタの更新と呼ばれる作業) ↓ひとつ前の commit & push によってリポジトリに変更が加わりNetlifyが検知してデプロイ開始されるといったようにCMSで変更を加えたらデプロイまでの流れをGithub Actionsで自動化した。
ビルド・デプロイ
AmplifyからNetlifyに移行した兼ね合いでビルド設定をtomlファイル用にした。
Netlifyだとコンソールでビルド設定書くのがやりにくいのでnetlify.tomlを作ることが多いのかも。
[build]command = "git submodule update --init --recursive && npm ci --cache .npm --prefer-offline && npm run build"publish = "dist"
[build.environment]NODE_VERSION = "22.13.0"実際の運用
記事を作成する方法は大きく分けて2つある。
1. CMSで記事を書く
管理画面アクセス後、 「新規作成」を押下して記事を作成する。

admin/config.ymlにある設定ファイルに記述したフィールドがこのようにGUIとして表示される。

ここに入力した値はマークダウンファイルのフロントマターと呼ばれるエリアに展開されることになる。
Astro Content Collectionではそのフロントマターに記述されたメタ情報を取得できるので、 その情報を取得して記事へ描画している。
画像に関してはCMSに画像をインポートすることができ、そこから画像を選択し表示することが可能で、画像の配置場所もadmin/config.ymlで決めることができるがcontent/imageに格納するように設定している。
ただ、画像はpublicディレクトリに配置したいのでビルド時にcontent/imageをpublicディレクトリにコピーしている。
保存すると記事のタイトルがそのままslugに反映されるが、変更したい場合は記事編集画面の右上三点リーダーにてスラッグの編集が可能。

保存後、マークダウンファイルがリポジトリにコミットされる。
レスポンシブも対応されており、スマホで書こうと思えばかけるようになっている。
2. ローカル(hygen)
SveltiaCMSはDecapCMSよりUI/UXが改善されたように思えるが、それでも書き心地が悪いと思うことがある。

特にこのサイドメニューがリサイズできず、入力領域が狭い状態で書かなきゃいけないことにストレスを感じるため、たまにローカルで書くこともある(VSCodeのマークダウンプレビューが備わっているし、ローカルサーバー起動すればリアルタイムにブログに文字を反映しながらかけるので体験はいい)。
記事を書く際にフロントマターの記述などを意識したくないので、npm run new:postというコマンドを用意しており、このコマンドをたたくと予め入力しておきたい値が求められ、その情報をもとにマークダウンファイルが生成されるようにしている。

AstroとCMSとの連携・認証
このあたり実装途中あんまりメモ取れなかったのと、深く理解できていない部分はあるのでさらっと書く。
Decap CMS
- npmモジュールなどは不要
public/admin/index.htmlを以下のドキュメントに沿って作成するだけで管理画面が作成完了する。Astroならpages/admin/index.astroでもいいので自分はそうしている。
- 管理画面のつなぎこみ設定やメタデータ管理などはconfig.ymlで行う
- CMSに書き込み許可するためgithubログインが必要(ログインユーザーがつなぎこんだサブモジュールのprivateリポジトリへのアクセス権があれば記事の作成や更新などが可能)。
OAuth appとOAuth認証用のハンドラが必要だった(privateリポジトリでなければNetlify Identity + Git Gatewayで簡単にできたらしい)。ハンドラについてはさすがに作りたくなかったのでnpmのパッケージを使った。SSRモードじゃないと動かなかったので注意。
OAuth appについては
https://github.com/settings/developers
「OAuth app」から新規作成する。
callbackにはhttps://{自分のサイト}/oauth/callbackを追加する必要がある。
Sveltia CMS移行と注意点
移行は最小限で済んだ。 ドキュメントにある通り、CDNの読み込み先を変更するだけ。 (真っ白な画面になったりしたのでDecapからの移行時はキャッシュとか非同期で読み込んでみるとかで調整したほうがいいかもしれない)
OAuthのハンドラなどはSveltiaが内包しているようで、ひとつ前のセクションでやったことは不要になった。
OAuthのハンドラは不要になったが、OAuth appは新規で作成して、NetlifyのOAuth設定(client idとシークレットの登録)は必要
// OAuth appApplication name: 任意Homepage URL: https://{name}.netlify.app)Authorization callback URL: https://api.netlify.com/auth/doneSveltia CMSのエラーと回避策
Sveltia CMSで記事の新規作成時、「A path was requested for deletion which does not exist as of commit oid」エラーが出て記事を作成できなかった。
記事を全削除したり、slugや画像の日本語をすべて英字に変えたりしてみても解決せず、何が原因かわからないままこれまで使っていたblogとは別のコレクション(src/content/posts/test)を作成したらその中では記事が作成できた。
これまでposts直下にmdファイルを管理していたが、 blog用mdをblog等のサブディレクトリに移動・folderも合わせることでエラーが解消した。
おそらく/postsのなかに.github/workflowなどがあったのでこれが影響していそうではあるが、DecapCMSでは奇跡的にうまく動いてたので問題なかったが、DecapCMSから移行するときは一応注意したほうがよさそう。
ひとまず移行後は 管理画面UIが大幅改善して、モバイル対応・プレビュー崩れも少なく満足している。 下書きやslug編集もデフォルトで対応されていて(Decapにはなかった)、使いやすい。