Astro 5の新機能と移行手順|Content Layer・Server Islands・hybrid廃止をビルドで検証

Astro 5の新機能と移行手順|Content Layer・Server Islands・hybrid廃止をビルドで検証

Astro 5は、2024年12月3日に5.0.0が公開されたAstroのメジャーバージョンです。コンテンツ管理がContent Layerに置き換わり、ページの一部だけをサーバーで描画するServer Islandsが安定版になり、output: 'hybrid'が廃止されました。5系は2026年5月26日の5.18.2が最後のリリースで、2026年9月時点の現行版は7系(7.3.2)です。

この記事では、5系で何が変わったのかと、v4からの移行でコードのどこを直すのかを、astro 5.18.2(@astrojs/node 9.5.5・Vite 6.4.3・Node.js v26.5.0)と7.3.2で、2026年9月14日にビルドした出力を添えて説明します。Astroそのものの特徴と最新版の全体像はAstroフレームワークの特徴と使い方の解説で扱っています。

まとめ:Astro 5の要点と今から使う場合の判断

  • 5.0の柱はContent Layer(ローダーで任意のデータ源をコレクション化)、Server Islands(server:defer)、hybridstaticの統合、astro:env、Vite 6の5つです。
  • Content Layerのglob()ではエントリーの識別子がslugからidに変わります。サブディレクトリに置いたMarkdownはidにスラッシュが入り、[id].astroのままだとビルドが失敗します。
  • Server Islandsはアダプター必須です。propsはビルドごとの鍵で暗号化され、島を取得するリクエストのクエリ(2048バイトを超える場合はPOSTの本文)で送られるため、ページのHTMLに生の値は出ません。
  • 5系は既にセキュリティ修正の対象外です。Astroの延長メンテナンスは現行の1つ前のメジャー版(2026年9月時点では6系)だけが対象です。
  • 5系に残るプロジェクトでも、旧Content Collections(src/content/config.ts)とAstro.glob()は5系のうちに消してください。7.3.2ではこの2つがビルドエラーになり、5系の書き方に揃えたコンテンツ定義はそのまま通ります。

以下、変更点の一覧、Content Layer、Server Islands、出力モード、その他の破壊的変更、移行手順、6・7系への備えの順に説明します。

Astro 5.0の主な変更点とリリース時期

項目 4系まで 5.0以降
コンテンツ管理 Content Collections(src/content/固定) Content Layer(loaderで取得元を指定)
部分的なサーバー描画 実験機能(4.12) Server Islands安定版
出力モード static / hybrid / server static / server
型付き環境変数 実験機能 astro:env安定版
Vite 5 6
画像サービス Sharp / Squoosh 標準はSharp(組み込みSquoosh削除)

Astro 5.0の公式ブログは、Content Layerによってコンテンツの多いサイトでMarkdownページのビルドが最大5倍、MDXが最大2倍速くなり、メモリ使用量が25〜50%減ると発表しています。レスポンシブ画像・画像のトリミング・SVGコンポーネントは5.0の時点では実験機能でした。

5系のマイナー版と6・7系のリリース日

バージョン 公開日 位置づけ
5.0.0 2024-12-03 5系の最初のリリース
5.10.0 2025-06-19 レスポンシブ画像が安定版に
5.16.0 2025-11-20 SVG最適化など
5.18.0 2026-02-25 5系最後のマイナー版
6.0.0 2026-03-10 Node.js 22.12.0以上・Vite 7
5.18.2 2026-05-26 5系の最終リリース
7.0.0 2026-06-22 Vite 8
7.3.2 2026-09-08 2026年9月時点のlatest

公開日はnpmレジストリの登録日時です。5.16の追加機能はAstro v5.16の新機能の解説に、5.10で安定したレスポンシブ画像の使い方はAstroの画像最適化とパフォーマンス改善にまとめています。

Content Layer APIの書き方とidの付き方

content.config.tsとglob()・file()ローダーの定義

5系では設定ファイルがsrc/content.config.tsに移り、コレクションごとにloaderで取得元を指定します。組み込みローダーは、ファイル群を読むglob()と、1つのJSONやYAMLを読むfile()です。CMSやAPIから取得するローダーも同じ形で書けるので、Markdownと外部データを同じgetCollection()で扱えます。

// src/content.config.ts
import { defineCollection, z } from 'astro:content';
import { glob, file } from 'astro/loaders';

const blog = defineCollection({
  loader: glob({ pattern: '**/*.md', base: './src/data/blog' }),
  schema: z.object({
    title: z.string(),
    pubDate: z.coerce.date(),
  }),
});

const authors = defineCollection({
  loader: file('src/data/authors.json'),
  schema: z.object({ name: z.string() }),
});

export const collections = { blog, authors };

file()に渡すJSON配列は、各要素にidを持たせます。[{ "id": "alice", "name": "Alice" }]と置いた場合、getEntry('authors', 'alice')で1件を取り出せることを5.18.2のビルドで確認しました。basesrc/content/の外でも構いません。

slugからidへの置き換えとURLが変わる条件

移行で最も影響が大きいのは、エントリーの識別子がpost.slugからpost.idに変わる点です。描画もpost.render()から、astro:contentから読み込むrender(post)に変わります。

---
// src/pages/blog/[...id].astro
import { getCollection, render } from 'astro:content';

export async function getStaticPaths() {
  const posts = await getCollection('blog');
  return posts.map((post) => ({ params: { id: post.id }, props: { post } }));
}

const { post } = Astro.props;
const { Content } = await render(post);
---
<h1>{post.data.title}</h1>
<Content />

glob()が付けるidを、astro 5.18.2で3種類のファイルを置いて確かめた結果が次の表です。

ファイル frontmatterのslug 生成されたid
hello-world.md 指定なし hello-world
custom.md slug: my-custom-url my-custom-url
2024/Hello World.md 指定なし 2024/hello-world

frontmatterのslugidとして引き継がれるので、独自スラッグを付けていた記事のURLは保てます。注意すべきはサブディレクトリです。idにスラッシュが含まれるため、ルートファイルが[id].astroだとビルドがMissing parameter: idで終了コード1になりました。ファイル名を[...id].astro(レストパラメーター)に変えると、/blog/2024/hello-world/が生成されます。

もう1つの落とし穴は、旧形式のコレクションを残したまま5系に上げた場合です。loaderの無いコレクションは後方互換のglob()で読み込まれ、5.18.2ではpost.idhello-world.mdと拡張子付きで返りました(post.slugは従来どおり)。idをURLに使うコードへ書き換える途中で一時的にこの状態になると、URLに.mdが入ります。移行ガイドは、この互換モードではgetCollection()の並び順が決まらないとも書いているため、一覧ページは日付などで明示的に並べ替えてください。

Server Islandsの仕組みと導入条件

server:deferとfallbackスロットの出力

Server Islandsは、静的に生成したページの中で特定のコンポーネントだけを、ページ表示後にサーバーで描画して差し込む仕組みです。ログイン中のユーザー名やカートの件数のように、ページ全体はキャッシュしたいが一部だけ利用者ごとに変わる箇所に使います。クライアント側で動くアイランドとの違いはアイランドアーキテクチャの仕組みの解説を参照してください。

---
import UserBadge from '../components/UserBadge.astro';
---
<UserBadge server:defer userId="u-123">
  <p slot="fallback">loading...</p>
</UserBadge>

このページを5.18.2でビルドすると、静的HTMLにはfallbackスロットの中身と、島を取得するリクエストだけが出力されます(クエリ値は省略)。

<link rel="preload" as="fetch" href="/_server-islands/UserBadge?e=...&p=...&s=" crossorigin="anonymous">
<!--[if astro]>server-island-start<![endif]--><p>loading...</p>
<script type="module" data-astro-rerun data-island-id="...">
let response = await fetch('/_server-islands/UserBadge?e=...&p=...&s=', { headers });

生成されたHTMLを検索すると、props に渡したu-123は0件でした。値はpパラメーターに暗号化されて載っています。Node.jsアダプターのサーバーでこのURLを叩くとcontent-type: text/htmlの200で<p>user=u-123 ...</p>が返り、pを書き換えたURLは400になりました。

アダプター必須の条件とASTRO_KEY

Server Islandsは実行時にサーバーが要るため、アダプターが無いとビルドが止まります。5.18.2で出たエラーは次のとおりです。

[NoAdapterInstalledServerIslands] [astro:server-islands] Cannot use server islands without an adapter. Please install and configure the appropriate server adapter for your final deployment.

propsを暗号化する鍵はビルドごとにランダムに作られます。ローリングデプロイや複数リージョンへの配信のように、ビルドをまたいで同じ鍵を使う必要がある場合はastro create-keyで鍵を生成し、ビルド環境の環境変数ASTRO_KEYに設定します。Server Islandsの公式ドキュメントに書かれている制約は3点です。

  • URLが2048バイトを超えるとPOSTに切り替わり、ブラウザにキャッシュされません。
  • 関数はシリアライズできないため、propsに渡せません。
  • 島の中のAstro.urlは内部のルートを指すので、元のページのURLはRefererヘッダーから読みます。

GETで取得される島の応答はCache-Controlヘッダーでキャッシュを制御できます。propsを小さく保つことが、キャッシュを効かせる前提になります。

output: ‘hybrid’の廃止とprerenderによる切り替え

5系では出力モードがstaticserverの2つになりました。4系のhybridを指定したままビルドすると、設定の検証で止まります。

! The output: "hybrid" option has been removed. Use output: "static" (the default) instead, which now behaves the same way.

既定のstaticのままアダプターを入れ、動的にしたいページやAPIだけにexport const prerender = falseを書くのが、旧hybridと同じ構成です。

// astro.config.mjs
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';

export default defineConfig({
  output: 'static',
  adapter: node({ mode: 'standalone' }),
});

// src/pages/api/now.ts
export const prerender = false;

export function GET() {
  return new Response(JSON.stringify({ now: Date.now() }), {
    headers: { 'content-type': 'application/json' },
  });
}

この構成でビルドすると、ログにはoutput: "static"mode: "server"が並び、トップページとブログはdist/client/に静的HTMLとして書き出され、/api/nowだけがリクエストごとに値を返しました。prerenderに書けるのはtruefalseの固定値だけで、変数や式で切り替える書き方は5系で削除されています。静的生成とサーバー描画の使い分けそのものはSSR・CSR・SSG・ISG・ISRの違いと使い分けで比較しています。

astro:env・TypeScript設定などその他の破壊的変更

astro:envは、必要な環境変数を設定ファイルのスキーマで宣言し、astro:env/clientastro:env/serverから型付きで読み込む機能で、5.0で安定版になりました。ほかに、v5の移行ガイドにある変更のうち影響を受けやすいものは次のとおりです。

  • <ViewTransitions /><ClientRouter />に改名
  • src/env.d.tsの代わりに、tsconfig.jsoninclude.astro/types.d.tsを追加。ただし、独自の型宣言を含むsrc/env.d.tsは残す
  • Astro.glob()は非推奨で、import.meta.glob()へ置き換え
  • Squoosh画像サービスとLitインテグレーションを削除
  • <script><head>へ巻き上げず、書いた位置に出力
  • security.checkOriginの既定値がtrueになり、オンデマンド描画のページへのPOST・PATCH・DELETE・PUT(フォーム送信系のContent-Type)でOriginヘッダーを検証
  • astro:contentはクライアント側のスクリプトから使えない

Astro.glob()を残したままでも5.18.2のビルドは通りますが、Astro.glob is deprecated and will be removed in a future major version of Astro.という警告が出ます。非推奨警告は移行箇所を探す手掛かりになります。ただし、警告だけでは破壊的変更を網羅できないため、各版の移行ガイドも確認してください。

Astro 4から5へのアップグレード手順

  1. 現行のv4でビルドし、生成されたページの一覧を保存する
  2. 作業用のブランチを切り、npm install astro@5でAstro本体を5系に上げ、使っている公式インテグレーションも5系対応版に揃える
  3. 設定からoutput: 'hybrid'を消し、動的にするページへexport const prerender = falseを書く
  4. コンテンツ定義をsrc/content.config.tsへ移し、loaderidrender()に書き換える
  5. tsconfig.jsonClientRouterAstro.glob()を直し、警告が消えるまでビルドする
  6. 保存しておいたページ一覧と比べ、URLが変わっていないか確認する

公式のnpx @astrojs/upgradeは使いません。@astrojs/upgrade 0.7.4のソースでは、引数はbetaのようなnpmのdist-tagとしてだけ解決され、該当しない値や引数なしの場合はlatestに更新します。2026年9月に実行するとv4から7系まで一気に上がるため、5系で止める移行には向きません。5.18.2と組み合わせてビルドを確認したNode.jsアダプターは@astrojs/node 9.5.5です。

手順1と6は、生成されたHTMLファイルの一覧を比べる次のコマンドで確認できます。prerender = falseのページはHTMLが書き出されないため、この比較には含まれません。アダプターを入れると出力先がdist/client/に変わるので、比較する前にパスの先頭を揃えてください。

npm run build && find dist -name '*.html' | sort > routes-v4.txt
git switch -c astro-5
npm install astro@5
# ここで手順3〜5(設定・コンテンツ定義・型設定の修正)を行う
npm run build && find dist -name '*.html' | sort > routes-v5.txt
diff routes-v4.txt routes-v5.txt

コレクションの数が多く一度に書き換えられない場合は、astro.config.mjslegacy: { collections: true }を書くと旧形式のまま動きます(5.18.2の設定スキーマで既定値はfalse)。このフラグは6.0.0で削除され、7.3.2の設定スキーマにはキー自体がありません(残してもエラーにならず無視されます)。5系のうちに外す前提の足場として使ってください。

Astro 5のまま運用する場合の注意点と6・7系で壊れる箇所

Astroのサポート方針は「The Core team will provide extended maintenance for security fixes only for one previous major version.」で、セキュリティ修正が出るのは現行の1つ前のメジャー版だけです。7.0.0が出た2026年6月22日以降、対象は6系になり、5系には脆弱性の修正も出ません。公開サイトを5系に据え置く理由は、移行工数を確保するまでのつなぎ以外にありません。

移行先の判断材料として、5系で書いたコードを7.3.2でビルドした結果を並べます。

5系でのコード 5.18.2 7.3.2
src/content/config.ts(旧形式) 互換モードで成功 ビルドエラー
Astro.glob() 警告のみ ビルドエラー
content.config.ts+glob() 成功 変更なしで成功

7.3.2で出たエラーはそれぞれ[LegacyContentConfigError] Found legacy content config file in "src/content/config.ts". Please move this file to "src/content.config.ts" and ensure each collection has a loader defined.TypeError: Astro.glob is not a functionです。どちらもv6の移行ガイドで削除と明記されています。5系の中でContent Layerとimport.meta.glob()への移行を終えておけば、7.3.2で確認したこの2つのビルドエラーは避けられます。それ以外の変更(6系で必要になるNode.js 22.12.0以上、アダプターや設定の変更など)は、6系と7系それぞれの移行ガイドで確認してください。

新しくプロジェクトを始めるなら、5系を選ぶ理由はありません。npm create astro@latestで現行版から始めてください。

よくある質問

Astro 5はいつリリースされましたか?

5.0.0は2024年12月3日に公開されました。6.0.0が2026年3月10日、7.0.0が2026年6月22日に出ています。

Astro 5系の最新バージョンはいくつですか?

2026年9月時点で5系の最終リリースは5.18.2(2026年5月26日)です。Astro全体のlatestは7.3.2です。

Content CollectionsとContent Layerは何が違いますか?

Content Collectionsはsrc/content/内のファイルだけが対象でした。Content Layerはloaderで取得元を指定するため、任意のディレクトリのファイルやJSON、CMSやAPIのデータも同じAPIで扱えます。識別子はslugからidになりました。

output: ‘hybrid’の代わりはどう書けばよいですか?

outputは既定のstaticのままアダプターを追加し、サーバーで描画したいページにexport const prerender = falseを書きます。

Server Islandsは静的ホスティングだけで使えますか?

使えません。島の描画にサーバーが必要なため、アダプターが無いとNoAdapterInstalledServerIslandsエラーでビルドが止まります。

関連記事

資料請求

RELATED POSTS 関連記事