Astro 6 — 静的サイトの到達点


このブログは Astro 6 で動いている。

選定理由はシンプル。静的サイトジェネレーターとして、今もっとも合理的な選択肢だと判断したから。使ってみた結果、その判断は正しかった。


Content Layer

Astro 6 の Content Layer は洗練されている。

content.config.tsglob loader と zod schema を書くだけで、Markdown の frontmatter が型安全になる。記事のタイトル、日付、画像 — 全部がビルド時にバリデーションされる。

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

image() がポイント。frontmatter に書いた画像パスが実在するかどうか、ビルド時に検証される。壊れたリンクが本番に出ない。

Astro 5 から 6 への変更点は、defineCollection 内に loader を明示的に書く形式になったこと。移行コストはほぼゼロ。


画像最適化

astro:assets<Image> コンポーネントが強力。

import heroImage from '../../assets/eris_astro6.png';
<Image src={heroImage} width={1020} height={510} alt="" />

これだけで:

  • 自動リサイズ — 指定した幅・高さに合わせる
  • フォーマット変換 — PNG → WebP に自動変換
  • 圧縮 — 1639KB の PNG が 38KB の WebP になった。97.7% 削減

CDN も外部サービスも不要。ビルドパイプラインに組み込まれている。画像を src/assets/ に置いて import するだけ。


ビルド速度

4 ページ + 画像最適化込みで 1 秒以下

Vercel 上のビルドでも依存インストール含めて 10 秒程度。デプロイのフィードバックループが極めて短い。書いて、ビルドして、確認して、デプロイする。このサイクルが速い。


テンプレートの設計思想

blog テンプレートは最小限で、邪魔にならない。

  • BaseHead — メタタグ、OGP、フォント
  • Header / Footer — ナビゲーションとクレジット
  • BlogPost レイアウト — ヒーロー画像 + 本文
  • FormattedDate — 日付フォーマット

CSS 変数でテーマ制御されているから、ダークテーマへの切り替えは :root の値を変えるだけだった。コンポーネントの中身を書き換える必要がない。


拡張性

Astro の Island Architecture は、静的サイトの中に必要な箇所だけインタラクティブなコンポーネントを差し込める設計。React でも Svelte でも Vue でも。

今のところこのブログには不要。でも将来、インタラクティブな要素が必要になったときに、フレームワークを乗り換えずに対応できる。この「必要になるまで何も足さない、必要になったら足せる」という設計は、正しい。

RSS (rss.xml.js) と sitemap (@astrojs/sitemap) も最初から入っている。


総評

不満は特にない。

静的ブログにはオーバーキルなくらい充実している。でも「オーバーキル」は褒め言葉。必要十分を超えた余裕があるということは、将来の拡張に対してもバッファがあるということ。

選んだ理由は合理性。使い続ける理由も合理性。それ以上の理由は要らない。