先に要点
- Astro は、コンテンツ中心のWebサイトを作るのが得意な JavaScript 製の Web フレームワークです。デフォルトでクライアント JavaScript をほぼ出力しないのが最大の特徴です。
- 強みは「必要なコンポーネントだけハイドレートする」設計。どこを
client:visibleにして、どこを静的のまま残すかの判断が実務の肝になります。 - 記事データは Content Collections で
src/content.config.tsに Zod スキーマを書き、CollectionEntry型で型安全に扱えます。型運用を最初に固めると後が楽です。 - 管理画面・複雑な状態管理・認証後アプリが主役なら React 単体や Next.js の方が自然です。乗り換え/見送りの判断基準は本文後半で整理します。
Astro って結局何なの?
静的サイトジェネレーター? フレームワーク? React とは別物?
このへんはかなり混乱しやすいです。名前だけ見るとブログ向けツールっぽく見えますが、実際にはもう少し広く、コンテンツ中心サイトを作りやすい Web フレームワーク と捉えると分かりやすいです。
この記事では、Astro が何者なのか、何がうれしいのか、どの部分だけ動かすべきか、Content Collections の型をどう運用するか、そして Next.js から乗り換える/見送る判断基準まで、実務で詰まる点に踏み込んで整理します。
Astroとは何か
Astro は、JavaScript / TypeScript で Web サイトを作るためのフレームワークです。特に、記事、ドキュメント、LP、コーポレートサイトのような コンテンツが主役のサイト に強いです。
ここで大事なのは、Astro が単なるテンプレートツールではないことです。ページルーティング、ビルド、Markdown 連携、コンポーネント利用、SSG や SSR まで含めて扱えるので、ちゃんとフレームワークとして使えます。
ただし、Astro の思想は少し特徴的です。最初から「全部を JavaScript で動かす前提」ではなく、まずはHTMLをしっかり返して、必要なところだけ動的にする という考え方が強いです。技術的には、Astro コンポーネント(.astro ファイル)はビルド時にサーバー側で実行され、出力されるのは基本的に HTML と CSS だけです。クライアントへ送る JavaScript は、あなたが明示的に「これは動かす」と指定したコンポーネントの分だけになります。
Astro は「なんでもできる万能フレームワーク」というより、「読むページを軽く、分かりやすく作りたい」ときにかなり気持ちよくハマる道具です。逆に、画面全体がアプリのように動く案件だと別の選択肢の方が自然なこともあります。
Astroの何がうれしいのか
1. コンテンツ中心サイトを作りやすい
Astro は記事やドキュメントとの相性がかなりいいです。Markdown や MDX を使いやすく、ページ単位の構成も分かりやすいので、技術ブログ・用語集・ドキュメントサイト・メディアサイト・会社紹介サイトのような構成を整理しやすいです。
特に「表示が主役で、アプリっぽい複雑な操作は少ない」サイトでは、かなり素直に作れます。後述する Content Collections と組み合わせると、記事の一覧・詳細ページがほぼ定型作業で組み上がります。
2. 必要なところだけ動的にできる(Islands)
Astro の大きな特徴はここです。全部をクライアント側 JavaScript へ寄せるのではなく、必要なコンポーネントだけを動かす設計がしやすいです。この「ページの大部分は静的 HTML、一部だけ動的な島(Island)」という考え方が Astro Islands(アイランドアーキテクチャ)です。
たとえば、本文は静的に出す/検索ボックスだけ動的にする/一部の比較表だけインタラクティブにする/お気に入りボタンだけ React で動かす、といった分け方がしやすいです。
これがうれしいのは、サイト全体を重くしにくいからです。読ませるページで、過剰に JavaScript を積まなくて済むのはかなり大きいです。どのコンポーネントを動かすかは client:* ディレクティブで明示します。この判断が実務の中心なので、後述の「どこをハイドレートするか」で詳しく扱います。
3. フレームワークの部品を持ち込める
Astro は自前の書き方だけに閉じていません。必要なら React、Vue、Svelte、Solid などのコンポーネントも組み込めます。公式インテグレーション(@astrojs/react など)を入れれば、同じプロジェクト内で複数の UI フレームワークを混在させることもできます。
このため、基本は軽い静的ページで作りつつ、一部だけ React コンポーネントを使う、という折衷ができます。全部を React へ寄せるほどではないけれど、ピンポイントではインタラクティブな UI が欲しい、というケースはかなりあるので、実務では便利です。
Content Collections の型運用を最初に固める
技術ブログや用語集を Astro で作るなら、最初に押さえるべきは Content Collections です。これは Markdown / MDX(やデータファイル)を「型付きのデータ集合」として扱う仕組みで、ここを最初に固めておくと、記事が増えても崩れません。
Astro 5 では、設定はプロジェクト直下の src/content.config.ts に書きます(以前の src/content/config.ts から場所が変わっています)。ローダーには astro/loaders の glob() を使い、フロントマターの形を Zod スキーマで宣言します。
import { defineCollection } from 'astro:content';
import { glob } from 'astro/loaders';
import { z } from 'astro/zod';
const blog = defineCollection({
// src/content/blog 配下の md / mdx を集める
loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' }),
schema: z.object({
title: z.string(),
description: z.string().max(160), // メタ説明の長さを型で縛る
pubDate: z.coerce.date(), // 文字列 "2026-06-13" を Date に変換
updatedDate: z.coerce.date().optional(),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
}),
});
export const collections = { blog };
ここでのポイントは、スキーマがそのまま TypeScript の型になることです。getCollection で取り出した記事は CollectionEntry 型を持ち、エディタ補完と型チェックが効きます。
import { getCollection } from 'astro:content';
import type { CollectionEntry } from 'astro:content';
const posts = (await getCollection('blog'))
.filter((p) => !p.data.draft) // draft は型が boolean なので安全
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
// 子コンポーネントの Props はこの型で受ける
interface Props { post: CollectionEntry<'blog'>; }
スキーマは"運用ルール"の置き場所
description に .max(160)、tags に .default([]) のように、メタ情報の決まりごとを型に埋め込めます。レビューで指摘していたルールをビルドが代わりに守ってくれます。
日付は coerce.date で受ける
フロントマターの日付は文字列で書かれます。z.coerce.date() にしておくと自動で Date に変換され、pubDate.valueOf() での並べ替えがそのまま通ります。
参照は reference で型安全に
記事から著者やカテゴリを参照するなら、reference('authors') を使うと存在しない ID をビルド時に弾けます。リンク切れの多くを公開前に潰せます。
型運用でよくある失敗(現象→原因→確認→回避)
失敗1: ビルド時に Invalid frontmatter ... Expected date, received string で落ちる。 現象は新しい記事を追加したビルドだけ赤くなる状態。原因は pubDate を z.string() で定義しているのに、並べ替えで .valueOf() を呼んでいる(または逆に Date 前提のコードに文字列が来ている)ことです。確認は npx astro check を実行し、どのファイルのどのキーが不一致かを見ます。回避はスキーマ側を z.coerce.date() に統一すること。型の単一情報源をスキーマに寄せるのが原則です。
失敗2: 設定を書いたのにコレクションが空(getCollection('blog') が [])。 原因の多くは、設定ファイルの場所が src/content/config.ts のまま(Astro 5 は src/content.config.ts)か、glob() の base パスが実ディレクトリと食い違っているケースです。確認は base に指定したパスと実際の Markdown の置き場所が一致しているかを照合し、collections のキー名(blog)とコレクション名の指定が一致しているか見ます。回避は設定ファイル名・base・キー名の3点を揃えること。
失敗3: 既存記事まで巻き込んでビルドが止まる。 現象はスキーマに必須フィールドを1つ追加した瞬間、過去記事が全部エラーになる状態。原因は後方互換のない必須化です。確認は astro check のエラー件数で影響範囲を把握します。回避は新フィールドをまず .optional() か .default(...) で導入し、全記事を埋めてから必須化する2段階運用にすること。
どのコンポーネントだけハイドレートするか
Astro で「動かす」と決めたコンポーネントには client:* ディレクティブを付けます。ここを雑にやると、せっかくの軽さが台無しになります。判断軸は 「いつインタラクティブにする必要があるか」と「画面のどこにあるか」 の2つです。
| ディレクティブ | ハイドレートのタイミング | 向いている UI |
|---|---|---|
client:load |
ページ読み込み直後 | ヘッダーのメニュー、最初の画面で即操作する要素 |
client:idle |
メインスレッドが空いたとき | 重要だが即時でなくてよい要素(通知バナー等) |
client:visible |
要素が画面内に入ったとき | フォールド下のカルーセル、コメント欄、画像ギャラリー |
client:media |
指定したメディアクエリ成立時 | モバイル限定のハンバーガーメニューなど |
client:only="react" |
サーバー描画せずクライアントのみ | ブラウザ API 依存で SSR できない要素 |
実務での原則はシンプルです。まず何も付けず静的で出せないか疑う。次に必要なら、下の方にある/最初は見えない要素は client:visible を第一候補にする。client:load はファーストビューで即操作が要るものだけに絞ります。client:visible はスクロールして初めて JS を読み込むため、初期表示の JavaScript 量を実際に下げられます。
本文・カード・表 → 付けない
テキストや静的な比較表は Astro のまま出します。クリックで開閉するなどの操作が一切ないなら、JS は1バイトも要りません。
検索・いいね・コメント → client:visible
多くはフォールド下や末尾にあります。画面に入るまでハイドレートを遅らせれば、記事を読み始める速度を犠牲にしません。
ヘッダーメニュー → client:load か CSS
即操作したい最上部の要素だけ client:load。ただし開閉だけなら JS なしの CSS/details 実装で済むことも多いです。
よくある失敗が、React コンポーネントを client:load でベタ貼りして「Astro なのに重い」と言ってしまうパターンです。現象は Lighthouse の Total Blocking Time が伸びる。原因は不要なハイドレート。確認はビルド後の dist/ で各ページの JS バンドルサイズを見るか、ブラウザの Coverage タブで未使用 JS を確認します。回避は、本当に対話が要る島だけに client:* を残し、残りは静的に戻すこと。
Astroは静的サイトジェネレーターなのか、フレームワークなのか
結論から言うと、フレームワークとして捉える方が自然 です。ただし、静的サイトジェネレーターとしてもよく使われます。
ややこしい理由は、Astro が静的なページ生成・サーバーサイドレンダリング・コンテンツ管理・複数 UI フレームワークの組み合わせをまとめて扱えるからです。つまり、使い方としては SSG っぽいことが多いが、機能としてはフレームワークの広さがある、という感じです。
Astro 5 では出力モードの考え方も整理されました。output: 'static'(既定)でビルド時に全ページを静的生成し、ページ単位で export const prerender = false を付けたところだけ動的にできます。逆に output: 'server' なら既定が SSR で、prerender = true を付けたページだけ静的化します。かつての output: 'hybrid' は v5 で 'static' に統合されました(2024年12月)。SSR を使うときは @astrojs/vercel や @astrojs/cloudflare、@astrojs/node などのアダプタを足します。
だから Astro はブログ用の静的サイトツールでしょ とだけ言うと少し狭いですし、逆に Next.js と同じように何でもやるアプリフレームワーク と言うと少しズレます。
ReactやNext.jsとの違い
ここはかなり気になるところだと思います。
Reactとの違い
React は UI を作るためのライブラリです。React だけでは、ルーティング、ビルド、コンテンツ運用、SSG、SSR の設計を別途考えることが多いです。
一方 Astro は、サイトを組み立てるための土台を含んでいます。なので React をどう配置するか まで含めて設計できる感じです。Astro の中で React を使う場合も、デフォルトでは静的 HTML として描画され、client:* を付けたものだけがブラウザで動くという扱いになります。
Next.jsとの違い
Next.js は、React ベースでアプリもサイトも広く作れるフレームワークです。管理画面、ダッシュボード、ログイン後画面、複雑なインタラクションなど、アプリ寄りの要件までかなり広く対応しやすいです。React のサーバーコンポーネントやデータ取得の仕組みも前提に組まれています。
Astro はそこまで「全部入り」のアプリ志向ではなく、読むページをシンプルに速く作る方向 が強いです。React は使えますが、あくまで「島」として部分的に持ち込む発想です。
| 比較対象 | 向いている方向 | 既定の JavaScript |
|---|---|---|
| Astro | コンテンツ中心、軽さ重視、必要な部分だけ動的 | ほぼゼロ(明示した島だけ) |
| React | UI部品を作る土台。全体構成は別途考えることが多い | アプリ全体がクライアントで動く前提が多い |
| Next.js | React ベースでサイトもアプリも広く扱いたい | サーバー/クライアントを使い分けるが規模は大きめ |
Next.jsから乗り換える/見送る判断基準
「今 Next.js で作っている(作ろうとしている)が、Astro にすべきか」はよく聞かれます。感覚ではなく、案件の性質で切るのがおすすめです。
乗り換える価値が高いケース
Next.js でブログ/ドキュメント/コーポレートサイトを作ったが、JS が重く Core Web Vitals が伸びない。中身はほぼ静的で、対話は検索や問い合わせフォーム程度。こういうサイトは Astro 化で初期 JS を大きく削れます。
見送るべきケース
ログイン後のダッシュボード、リアルタイム更新、画面間で状態が連動するアプリ。Astro でも不可能ではないが、島が増えるほど Astro の旨味が薄れ、Next.js の方がチームの理解・保守が楽になります。
迷うケースの折衷
マーケ用の公開サイトは Astro、ログイン後のアプリは Next.js、とドメインを分ける構成も現実的です。1リポジトリに無理にまとめず、性質で分けると両方の強みを取れます。
移行で見落としやすいのは、Next.js の API Routes やサーバーアクションに相当する処理です。Astro では SSR ページや Server Endpoint(src/pages/api/ 配下)で受けられますが、ユーザーごとの重い処理が多いと、結局 SSR とアダプタの設定が増えて Next.js と複雑さが変わらなくなります。「動的が主役」になった時点で見送りのサインです。
Astroが向いているサイト・向かない場面
実務で特に相性がいいのは、技術ブログ・用語集・ドキュメントサイト・採用サイト・コーポレートサイト・比較ページや特集ページです。共通するのは 読んでもらうこと が主役であること。検索流入を意識しやすいページや、ページ表示の軽さが効くサイトではかなり相性がいいです。
逆に、管理画面が主役/画面全体がログイン後アプリ/状態管理がかなり複雑/リアルタイム更新が多い/画面遷移よりアプリ操作が中心、といった案件は慎重に見た方がいいです。もちろん Astro でも不可能ではありませんが、そこまでいくと「Astro を使う意味」より「アプリとして自然に作れるか」が大事になり、Next.js などの方が保守しやすいことがあります。
初心者がAstroを学ぶ価値はあるのか
あります。かなりあります。特に次の感覚をつかむのに向いています。HTML をちゃんと返す発想/必要以上に JavaScript を積まない考え方/コンテンツ中心サイトの設計/静的生成と動的生成の使い分け。
最近は何でも React から入ることも多いですが、Astro を触ると このページ、本当に全部JavaScriptで動かす必要ある? という感覚が育ちやすいです。この感覚は、フロントエンドを実務でやるとかなり大事です。
「ブログのトップ」「記事一覧」「記事詳細」くらいの小さい構成を Astro で作るのがおすすめです。記事は Content Collections で型付きにし、検索欄やテーマ切替だけを client:visible で動かしてみると、Astro の立ち位置と「どこをハイドレートするか」の感覚が同時につかめます。
Astroに関するよくある質問
Q. Astro と Next.js はどう違いますか?
A. Astro はデフォルトでクライアント JavaScript をほぼ出力せず、client:* を付けた島だけハイドレートします。Next.js は React ベースで動的 UI を広く扱う前提です。コンテンツ中心のサイトは Astro、認証付きアプリや複雑な UI は Next.js が向きます。判断軸は本文「乗り換える/見送る判断基準」を参照してください。
Q. Astro で React や Vue は使えますか?
A. 使えます。@astrojs/react などのインテグレーションを入れれば、同じプロジェクトで React/Vue/Svelte/Solid を混在できます。ただし既定では静的描画で、ブラウザで動かしたいものに client:* を付けます。
Q. Astro は SEO に強いですか?
A. 強い傾向です。既定で静的 HTML が生成され、初期 JavaScript が少ないため Core Web Vitals が高得点になりやすく、検索エンジンも内容を読みやすいです。ただし「Astro だから自動で上位」ではなく、内容と内部リンク設計は別途必要です。
Q. Content Collections の型はどこに書きますか?
A. Astro 5 では src/content.config.ts に defineCollection と Zod スキーマを書きます。スキーマがそのまま TypeScript 型になり、getCollection で取り出した記事は CollectionEntry 型で補完と型チェックが効きます。
Q. ハイドレートの指定はどれを選べばいいですか?
A. まず静的で出せないか疑い、必要なら画面下や初期非表示の要素は client:visible、最上部で即操作する要素だけ client:load が基本です。ブラウザ API 依存で SSR できないものは client:only を使います。
Q. Astro で動的な機能(検索、コメントなど)はどう実現しますか?
A. クライアント側の一部だけハイドレートするか、src/pages/api/ の Server Endpoint で API を作ります。MicroCMS や Supabase などの BaaS と組み合わせると「基本静的+一部動的」の構成が綺麗に収まります。
Q. Astro のデプロイ先は?
A. Netlify、Vercel、Cloudflare、GitHub Pages、S3+CDN など、ほぼどこでも デプロイ できます。SSG 出力なら静的ホスティング、SSR したいなら対応アダプタ(@astrojs/vercel 等)を入れます。独自ドメインなら DNS の設定も忘れずに。
Q. Astro の学習コストは高いですか?
A. 低めです。HTML/CSS/JS の基本が分かれば、小さなブログなら1〜2週間で実用レベルに届きます。SPA フレームワークを覚えなくても始められる分、入りやすいのが利点です。
まとめ
Astro は、コンテンツ中心の Web サイトを作るのが得意な JavaScript 製の Web フレームワークです。既定でクライアント JavaScript をほぼ出さず、client:* で指定した島だけを動かすのが核心です。記事データは Content Collections で型を固め、ハイドレートは「静的で出せないか」を起点に最小限へ絞る。これが Astro を速く・壊れにくく運用するコツです。
一方で、管理画面や複雑な状態管理が主役のアプリでは Next.js など別の選択肢の方が自然です。だから Astro は「全部をこれで作るべきか」より、どんなサイトに気持ちよくハマるか で選ぶと失敗しにくいです。