GatsbyからAstro(バージョン7)へ移行しました
2026.7.2 2026.7.20
この記事は約2 分で読めます
目次
筆者三好アキ(専門用語なしでプログラミング)
私について詳しくは → 自己紹介
GatsbyからAstroに移行
先日、本サイトをGatsbyからAstroに移行しました。
バージョンは、6月にリリースされたばかりのAstroバージョン7です。
Lighthouseの「Performance」スコアが「100」になるなど、スピードが大きく上がりました。

日本ではAstroを使っている人がまだ少ないので、少し今回のAstro移行の話を書いていきます。
Gatsbyとの6年間
本サイトでは、事業を立ち上げた2020年からずっとGatsbyを使っていました。
その時のバージョンはv2です。
いま過去のブログを読み返してみると、「Gatsbyのバージョンを2から3に上げた」、そして「3から4に上げた」という記事がそれぞれあります。
3から4に上げたのが2021年11月なので、5年近く何もしていなかったようです。
しかしこのGatsby、2022年リリースのバージョン5で開発が止まっています。
そのため「このままGatsbyを使い続ける」という選択肢はなく、「移行しないといけないな」とはここ数年ずっと思っていました。
が、そこまで強いモチベーションがなかったのも事実。
Gatsbyでも、そこまで危機的に困っていることはありませんでした。
「自サイトの刷新は優先順位が低い」という、よくある話です。
困り事を挙げるとすれば…
• 6年以上運用している中で、内部のコードがスパゲッティ化していたこと
• サイトデザインが一部古くなっていたこと
• Hot reloadの動きが不安定なこと
• ターミナルに依存関係のエラーが出ること
そして、buildに時間がかかること
Gatsbyに個人的な思い入れがあったことも、移行を先延ばししていた間接的な理由だったかもしれません。
初めてGatsbyを触ったとき、ページ遷移のあのスピードには感動しました。
今でも印象深く残っています。
些細なことですが、些細なことが記憶には強く残るものです。
それでも、サイト内のブログ記事を大きく整理したことをきっかけに、Astroへの移行もすることにしました。
Claude Codeで移行
移行作業の大部分で使ったのがClaude Codeです。
とはいえ、丸投げではなく、まずAstroのv7のドキュメントをClaude Codeに読み込ませ、出てきたコードをチェックしながら進める、という流れです。
私は以前Astroを使っていたことがあるので、Astroの基礎部分は知っていました。
しかし今年3月にv6が出て、それにキャッチアップする間もなく6月にv7が出たため、その2つのバージョンで加えられた変更に沿ったコードをClaude Codeに書かせていきました。
本サイトは静的サイトで、特に複雑なロジックはありません。
ベース部分の移行に1、2時間ほど、その確認・修正に数日、といった感じです。
AIツールで本当に開発の景色は変わったなと思います。
手間と時間が大きく短縮されました。
しかしそれは「調べる」、「コードを書く」というマニュアル作業の部分が大幅に減ったということが大きく、成果物の確認やチェックということには、これまでと同じ、あるいはこれまで以上に時間がかかっていると感じます。
以下、Gatsby → Astro移行について、コードと一緒に少し詳しく見ていきましょう。
一番大きく変わったのはデータの扱い方
GatsbyからAstroへの移行で最も大きく変わったのが、データ関係の部分です。
Gatsbyではデータ操作にGraphQLを使っていました。
マークダウンファイルの本文もfrontmatterも、すべてGraphQLのクエリ経由で読み込みます。
例えば全記事のパスと前後記事を取り出すには、gatsby-node.jsの中で次のようなクエリを書いていました。
// gatsby-node.js(Gatsby時代)
exports.createPages = async ({ actions, graphql }) => {
const { createPage } = actions
return await graphql(`
{
allMarkdownRemark(
sort: { fields: [frontmatter___id], order: DESC }
limit: 1000
) {
edges {
node {
fields { slug }
frontmatter { categoryTagJP categoryTagEN }
}
next { frontmatter { path title } }
previous { frontmatter { path title } }
}
}
}
`).then((res) => {
// ここで createPage を呼んでページを作る
})
}
GraphQLは便利ではありますが、frontmatterに項目を1つ足すたび、クエリにも同じ項目を書き足さないといけません。
それを忘れると、データが取れずエラーになります。
allMarkdownRemarkやedges、nodeといったGatsby独特の書き方も、久しぶりに触ると思い出すのに時間がかかります。
AstroではContent Collectionsを使うので、GraphQLはまるごと不要になりました。
Content Collectionsでは、まず記事のfrontmatterがどういう形なのかをsrc/content.config.tsで一度だけ定義します。
// src/content.config.ts
import { defineCollection, z } from "astro:content"
import { glob } from "astro/loaders"
const blog = defineCollection({
loader: glob({ pattern: "**/*.md", base: "./src/content/blog" }),
schema: ({ image }) =>
z.object({
id: z.string(),
path: z.string(),
title: z.string(),
categoryTagJP: z.string(),
categoryTagEN: z.string(),
// ...以下、必要な項目を並べていく
}),
})
export const collections = { blog }
あとは記事一覧を取り出したいところで、getCollection("blog")を呼ぶだけです。
前後記事のリンクも、GraphQLのnextやpreviousを使わず、取り出した配列を自分でソートして前後を決められます。
// src/pages/blog/[slug].astro
import { getCollection } from "astro:content"
const posts = await getCollection("blog")
// id順に並べて、前後の記事を決める
const sorted = [...posts].sort(
(a, b) => Number(a.data.id) - Number(b.data.id)
)
frontmatterに項目を足したいときも、content.config.tsのスキーマに1行足すだけです。
書き忘れれば型エラーで教えてくれるので、Gatsby時代のように「クエリに書き忘れて動かない」ということも起きません。
この部分はAstro移行でラクになった部分のひとつです。
つまずいたのはマークダウン内の画像
Astro移行で少し手間がかかったのが、マークダウンファイル内の画像とHTML記法の扱い方です。
Gatsby時代、私は記事の中に下記のような装飾付きのHTMLを直接書いていました。
<a class="amazon-book-card" href="https://www.amazon.co.jp/dp/...">
<div class="abc-img">
<img src="../../images/commonImg/react-book-ts-ai.svg" />
</div>
</a>
私の本の表紙画像を、Amazonへのリンクカードとして見せるためのもので、HTML記法で画像を表示しています。
画像はsrc内のimagesフォルダに置き、<img>タグの相対パスで読み込んでいました。
Gatsby内のマークダウンファイルでは、この書き方でも正しく表示されます。
一方、Astroではこの書き方が使えません。
Astroでsrc内の画像を使うには、その画像をimportする必要があるからです。
しかしマークダウンファイルでimportは当然使えません。
ここで<img src="../../images/...">のように相対パスで書いても、Astroはビルド時に画像として認識しないため、src以下の画像は表示されません。
かといって、HTMLブロックの中に通常のマークダウンファイルの画像表示の記法を書いても、画像として表示されないのです(下記コード参照)。
<a class="amazon-book-card" href="https://www.amazon.co.jp/dp/...">
<div class="abc-img">

</div>
</a>
HTMLブロックの中ではマークダウン記法が解釈されないからです。
この解決策はいくつかありました。
• 装飾部分をAstroコンポーネントにして記事から呼ぶ
• 独自記法をHTMLへ変換するremarkプラグインを書く
• 記事を.mdではなく.mdxにする
• 画像をpublicフォルダに移す
まず最初の2つの方法は、すぐに選択肢から消えました。
画像にそんな大袈裟なことをしたくないからです。
そして記事は現状の.mdで管理したいので、MDX化も選択肢から消えます。
最後のpublicフォルダを使う方法。これがもっとも楽です。
しかし少しためらいがあった理由は、画像をすべてsrc配下に入れておきたかったからです。
publicとの2箇所の管理はしたくなかったのです。
しかし考えてみると、マークダウン内でHTMLとともに表示させたい画像は私の本の表紙画像だけで、数にして35個ほど。
そしてそれは今後急激に増えることもない。
さらに画像は全てSVGなので、Astroの画像最適化の恩恵はなくてもいい。
なので、最終的にはpublic内に必要な画像を移しました。
public内の画像は<img src="/images/reactbook-ts-ai.svg" />のように、絶対パスでそのまま読み込めます。
HTMLの装飾もつけられます。
本文で![ ]()として使う画像はこれまでどおりsrcに置き、表紙画像のようなHTMLタグと一緒に装飾したい画像だけpublicに置く、という分け方に着地しました。
View Transitionsを入れて、外す
Astroには、ページ遷移をなめらかにするView Transitionsという機能があります。
ページ全体を再読み込みせず、必要な部分だけ差し替えて遷移させる機能です。
SPAのようにスムーズに動くようになります。
Gatsbyのあのスピーディなページ遷移が好きだった私としては、View Transitionsはぜひ入れたい機能でした。
なのでAstro移行作業が9割ほど終わったタイミングで、サイト全体に導入しました。
// src/layouts/Layout.astro
---
import { ClientRouter } from "astro:transitions"
---
<head>
<ClientRouter />
</head>
ところが問題が起こりました。
本サイトにはフォームがいくつかあり、そこではスパム対策にreCAPTCHAを使っています。
View Transitionsは通常のページ再読み込みをしないため、遷移でreCAPTCHAのトークン生成が壊れ、フォーム送信が403エラーになってしまうのです。
「フォームのあるページだけView Transitionsを無効にする」という回避策も考え、一度その仕組みも作りました。
しかし本サイトにはフォームが複数のページにあり、一部の部分が意図通りに動きません。
そこに別の仕組みを当てることも考えましたが、今回の移行の方針として「例外的なコードや機能は使いたくない」というのがありました。
今後長く運用していく中で必ず内部のコードは絡まっていくので、始まりの段階で複雑なことはしたくなかったのです。
なのでView Transitionsの導入は見送りました。
Deploy時に気をつけたこと
一番気を使ったのは、従来のGatsby版サイトのSEO評価への影響です。
特に注意したのがURL、具体的には末尾のスラッシュ(Trailing Slash)。
Gatsby版では、URL末尾のスラッシュ無しを正規のURL(canonical)としていました。
• スラッシュなし
https://monotein.com/blog/react-vite-how-to-use
• スラッシュあり
https://monotein.com/blog/react-vite-how-to-use/
Astro版でも末尾のスラッシュは消したかったため、astro.config.mjsに次のコードを加えました。
trailingSlash: "never",
しかしNetlifyでは、このようなフレームワーク側の設定を上書きするようにURLが変更されることがあります。
そのため、「Project configuration」→「Post processing」→「Pretty URLs」にある「Enable Pretty URLs」のチェックを外しています。

スラッシュひとつの有無でも、Googleは「異なるURL」と認識してSEOに大きな影響を与えるので、移行時には注意しましょう。
build時間が4分半から20秒に
ここからは移行して良かったことです。
一番大きいのはbuild時間の短縮。
Gatsby版ではNetlifyでのbuildに4分半かかっていました。
これがAstroに変えてみると20秒ほどになりました。
私のサイトは更新頻度も少ない単なる静的サイトなので、buildに時間がかかっても大きな問題ではなかったのですが、これほど短くなると、サイトを更新する時の妙な抵抗感は無くなります。
Gatsby時代は、記事の誤字脱字の修正といった微調整・微修正をしても、その直後にはbuildをしていませんでした。
そういう小さな修正がいくつか溜まってからbuildをかけていたのです。
しかし今は、どんなに小さな修正でも気楽にbuildをかけられるようになりました。
移行のついでに手を入れたこと
大枠の移行が終わったあとも、細かい改善をいくつか続けています。
例えばsitemapに、各記事の最終更新日(lastmod)を出すようにしました。
これはastro.config.mjsの中で、全記事のfrontmatterから更新日を読み取って組み立てています。
// astro.config.mjs(一部)
// 各記事のfrontmatterから更新日を読み、sitemapのlastmodに使う
const update = raw.match(/^update:\s*"([^"]+)"/m)?.[1]
const date = raw.match(/^date:\s*"([^"]+)"/m)?.[1]
const lastmod = update && update !== "none" ? update : date
Gatsbyのプラグインに任せていた部分を、Astroでは自分で書けるので、こうした細かい調整がやりやすくなりました。
また今回の刷新に合わせて、AIO/AI Optimization対策用のllms.txtの設置、著書一覧ページの新設(https://monotein.com/books)、全ページに構造化データ (JSON-LD)を設置といったことも行なっています。
おまけ:Prefetchはオンにしましょう
Astroでは、デフォルトでPrefetchがオフになっています。
Prefetchの有無で体感のスピードは大きく変わるので、快適なページ遷移のためにはオンにしておきましょう。
下記のコードでサイト全体でオンになります。
// astro.config.mjs
import { defineConfig } from "astro/config"
export default defineConfig({
prefetch: {
defaultStrategy: "viewport"
}
})
より細かなカスタマイズも可能です。
詳しくはAstro公式ページをご覧ください。

次に読みたい記事特集