【Astro】Pagefindで検索機能を追加する手順|静的サイトの全文検索

【Astro】Pagefindで検索機能を追加する手順|静的サイトの全文検索

Astroでブログは作れたけど、記事が増えてきて目当ての記事にたどり着けない…

静的サイトってサーバーがないのに、検索機能なんてつけられるの?

Astroのような静的サイトジェネレーターが出力するのは、ただのHTMLファイルの集まりです。
検索フォームを置いても、キーワードを投げる先のサーバーがありません。

そこで登場するのが Pagefind というツールです。
やっていることを一言でいうと、ビルドが終わったあとのHTMLを読んで、ブラウザだけで動く検索インデックスを作るというものです。

この記事では、AstroのサイトにPagefindで全文検索を足す手順を、導入から日本語まわりの注意点まで順番に整理していきます。

この記事でわかること
  • 静的サイトに検索をつけるときの選択肢と、それぞれの向き不向き
  • Pagefindがどのタイミングで何をしているのか
  • Astroへの導入手順(インストール・ビルド設定・検索UIの設置)
  • 日本語サイトでの注意点と、検索が動かないときの確認ポイント

このブログもAstroで動いていて、検索はPagefindを使っています。記事の管理まわりの話は別記事にまとめているので、あわせてどうぞ。

この記事の前提(2026年8月時点)
  • コードは Astro v5 系で動かしている構成をもとにしています
  • パッケージマネージャは pnpm ですが、npm・yarn でもコマンドを読み替えれば同じです
  • Pagefindの最新は 1.5系(1.5.0が2026年4月にリリース)。これから入れるなら、公式が案内している Component UI が既定の選択肢です
  • ただしこのブログ自体は 1.4系の Default UI のまま動かしているので、本記事では両方の書き方を載せます

公式ドキュメントは Pagefind にあります。

静的サイトに検索をつけるときの選択肢

まず、なぜ静的サイトの検索がちょっと面倒なのかという話からです。

WordPressのようなサーバーで動くCMSなら、検索はデータベースに問い合わせるだけで済みます。
一方、Astroでビルドしたサイトは配信するのがHTMLだけなので、問い合わせ先を自分で用意しないといけないわけです。

選択肢はだいたい3つに分かれます。

観点Pagefind外部の検索SaaS自前でJSONを作る
費用無料(自サイトに同梱)無料枠つきの有料サービスが多い無料
導入の手間◎ ビルド後に1コマンド△ 登録・APIキー・同期設定が必要△ 検索処理も自作
記事が増えたとき◎ 必要な部分だけ読み込む◎ サービス側の性能次第× 全件を読み込みがち
外部への依存なしあり(規約・料金の変更を受ける)なし
検索精度の調整○ 属性や設定で調整◎ 細かくチューニング可能△ 自分で実装した範囲
どれを選べばいい?
  • 個人ブログ・ドキュメントサイト … Pagefindが手軽でおすすめです
  • 大規模サイト・凝った検索体験が必要 … 外部SaaSを検討する価値があります
  • 記事数が数十件で、タイトル検索で十分 … 自前のJSONでも足ります

自前でJSONを作る方式がつらくなりやすいのは、記事が増えるほど最初に読み込むデータが重くなるところです。
その点Pagefindは、インデックスを分割して必要な部分だけを取りに行く設計になっているので、記事が増えても入口が重くなりにくいのが強みだと思います。

私もこのブログでは、外部サービスを増やしたくないという理由でPagefindを選びました。

Pagefindとは?ビルド後のHTMLからインデックスを作る仕組み

Pagefindは、静的サイト向けの全文検索ライブラリです。特徴的なのは動くタイミングで、公式ドキュメントにも「サイトがビルドされたあとにインデックスを作る」と書かれています。

流れにすると、こんな順番です。

Pagefindが動く順番
  • Astroがビルドして dist/ にHTMLを吐く
  • PagefindがそのHTMLを読んで、検索インデックスを dist/pagefind/ に作る
  • 読者がページを開き、検索ボックスに入力したタイミングでインデックスを読み込む

ポイントは、Pagefindが見ているのはAstroのソースではなく、出来上がったHTMLだということです。

つまりAstro専用のツールではありません。静的HTMLさえ出力できれば、他のフレームワークでも同じように使えます。
逆に言うと、ビルドしていない状態では検索が動かないということでもあります。ここは後半でもう一度触れます。

全文検索(full-text search)

タイトルだけでなく本文の中身まで対象にして探す検索のことです。「あの用語が出てきた記事、どれだっけ?」を探せるのが全文検索、という理解で大丈夫です。

らるじゅらるじゅ

「ビルド成果物を後から加工する」という発想が個人的にはかなり好きです。
フレームワーク側に手を入れなくていいので、乗り換えても資産が残るんですよね。

Astroに導入する手順

ここから実際の手順です。やることは大きく分けてインストール・ビルドコマンドの変更・検索UIの設置の3つです。

1. パッケージをインストールする

インデックスを作る本体を入れます。

pnpm add pagefind

pagefind はインデックスを生成するCLI本体です。必要なのは基本これだけで、検索UIもPagefindがインデックスと一緒に出力してくれます。

例外は、後述する1.4系までの Default UI を使う場合だけで、そのときは @pagefind/default-ui を追加でインストールします。

2. ビルドのあとにインデックスを作る

Pagefindはビルド後に動かすので、package.json のビルドスクリプトにつなげてしまうのが確実です。

{
  "scripts": {
    "build": "astro build && pagefind --site dist"
  }
}

--site にはビルド成果物のフォルダを渡します。Astroの初期設定なら dist です。

これで pnpm build を実行すると、dist/pagefind/ にインデックス一式が生成されます。
出力先のフォルダ名を変えたい場合は --output-subdir で指定できます(既定は pagefind)。

デプロイ先のビルドコマンドも忘れずに

Cloudflare PagesやVercelなどにデプロイしている場合、ホスティング側に設定したビルドコマンドでもPagefindが走る必要があります。
astro build だけを指定していると、ローカルでは検索できるのに本番だけ検索結果がゼロ、という状態になります。

3. 検索UIを設置する|1.5系のComponent UI

Pagefind 1.5.0 で追加された Component UI が、これから入れる場合の既定の選択肢です。

うれしいのは、UIのCSSとJSもPagefindがインデックスと一緒に出力してくれるところ。追加のnpmパッケージは要りません。

読み込むのはこの2ファイルです。

<link href="/pagefind/pagefind-component-ui.css" rel="stylesheet">
<script src="/pagefind/pagefind-component-ui.js" type="module"></script>

あとは、置きたい場所にカスタム要素を並べるだけです。

<pagefind-modal-trigger></pagefind-modal-trigger>
<pagefind-modal></pagefind-modal>

pagefind-modal-trigger が検索を開くボタン、pagefind-modal が検索モーダル本体になります。

Astroのレイアウトに入れるなら、こんな形です。

---
// src/layouts/Layout.astro
---

<html lang="ja">
  <head>
    <link href="/pagefind/pagefind-component-ui.css" rel="stylesheet" />
    <script is:inline src="/pagefind/pagefind-component-ui.js" type="module"></script>
  </head>
  <body>
    <pagefind-modal-trigger></pagefind-modal-trigger>
    <pagefind-modal></pagefind-modal>
    <slot />
  </body>
</html>
script には is:inline を付ける

Astroは script タグを既定でバンドル対象として処理します。
ここで読み込むJSはビルド後にPagefindが作るファイルなので、Astroのビルド時点では存在しません。is:inline を付けて、変換せずそのままHTMLに出してもらう必要があります。

4. 従来のDefault UIを使う場合|1.4系までの構成

1.4系までは @pagefind/default-ui というパッケージを読み込む形でした。
既存サイトがこの構成で動いているなら、無理に移行しなくても引き続き使えます。

Astroコンポーネントとして切り出しておくと、サイドバーでもヘッダーでも使い回せます。

---
// src/components/SearchBox.astro
import "@pagefind/default-ui/css/ui.css";
---

<div class="search"></div>

<script>
  // @ts-ignore(このパッケージは型定義を持たない)
  import { PagefindUI } from "@pagefind/default-ui";

  new PagefindUI({
    element: ".search",
    showSubResults: true,
  });
</script>

element にはUIを差し込む要素のセレクタを渡します。ここでは .search の div が検索ボックスに置き換わります。

new PagefindUI() に渡せるオプションのうち、よく使いそうなものを挙げておきます。

オプション既定値内容
element—UIを差し込む要素のセレクタ
showSubResultsfalse1ページ内の見出し単位の結果も出す
showImagestrue結果に画像を表示する
pageSize5一度に表示する件数
resetStylestrueUI部分にCSSリセットを当てる
translations言語から自動検出「検索」などのUI文言を差し替える
debounceTimeoutMs300入力から検索実行までの待ち時間

showSubResults を有効にすると、長い記事のどの見出しにヒットしたかまで出るので、記事が長くなりがちなブログとは相性が良いと思います。

なお、このパッケージを入れるときは pagefind@1.4 のようにバージョンを合わせておくと、本体とUIの世代がずれません。

5. 動作確認はビルドしてから

冒頭で触れたとおり、インデックスはビルド後に作られます。
つまり astro dev の開発サーバーでは検索が動きません。確認するときはビルドしてからプレビューします。

pnpm build
pnpm preview

devサーバーで動かないから設定ミスだと思って、しばらく溶かした…

らるじゅらるじゅ

これ、最初はだいたい引っかかるポイントだと思います。
「検索の確認はbuild後」とメモしておくと平和です。

日本語サイトでPagefindを使うときのポイント

日本語で使う場合に押さえておきたいのは、言語の判定方法です。

Pagefindは、ページの html 要素の lang 属性を見て言語を判定します。
インデックスを作るときと、ブラウザで初期化するときの両方で同じ属性をチェックする作りになっています。

<!doctype html>
<html lang="ja">
  <!-- ... -->
</html>

Astroのレイアウトでは、この1行が入っているかどうかだけ確認しておけば大丈夫です。

なぜ言語判定が必要なのか

英語のようにスペースで単語が区切られる言語と違い、日本語は文章がひと続きです。
そのため、どこで単語を区切るかを決める処理(セグメンテーション)が必要になります。Pagefindは日本語・中国語・韓国語向けにこの処理を持っていて、lang を見て切り替えています。

もう1つ知っておきたいのが、検出した言語ごとに独立したインデックスが作られるという挙動です。日本語のページと英語のページが混在していても、それぞれ別々にインデックスされます。

サイト全体を1つのインデックスにまとめたい場合は、この分割を無効にする設定があります。書き方が2通りあるので、指定する場所に注意してください。

指定場所書き方
設定ファイル(pagefind.yml)force_language: ja
CLIオプション--force-language ja

設定ファイル側はアンダースコア、CLI側はハイフンです。ここを取り違えると設定が読まれないまま動いてしまいます。

CJK対応はリリース版によって差がある

日本語・中国語・韓国語のセグメンテーションは、対応しているリリースでのみ有効です。
npx pagefind やnpmの pagefind パッケージで入るものは既定で対応版なので、通常はそのまま使えば問題ありません。他の配布経路から入れる場合だけ注意が必要です。

Pagefindの導入でハマりやすい3つのケース

導入したあとに引っかかりやすいところをまとめておきます。

検索結果に「サイドバーの文言」ばかり出てくる

Pagefindは既定で body の中身をインデックスします。
そのため、全ページ共通のパーツが入っていると、それも検索対象になってしまいます。

対処は2通りです。

方法書き方向いている場面
対象を絞る本文の要素に data-pagefind-body記事本文だけを検索させたい
除外する除きたい要素に data-pagefind-ignore一部のパーツだけ外したい

なお nav や footer、script、form といった要素は既定で自動的にスキップされます。共通パーツを適切な要素で組んでいれば、そもそも困らないことも多いです。

data-pagefind-body の落とし穴

この属性は、サイト内のどこか1ページにでも存在すると、属性が付いていないページはインデックスされなくなります。
記事ページだけに付けて固定ページに付け忘れると、固定ページが検索に出てこない状態になります。使うなら全ページに入れる前提で設計しましょう。

CSSセレクタでまとめて除外したい場合は、pagefind.yml という設定ファイルに exclude_selectors を書く方法もあります。

# pagefind.yml
site: dist
exclude_selectors:
  - "#sidebar"
  - ".ad-slot"

検索しても1件もヒットしない

いくつか順番に確認していくと、だいたい原因が絞れます。

ヒットしないときの確認順
  • dist/pagefind/ が生成されているか(=Pagefindが実行されたか)
  • ビルドしたものをプレビューしているか(開発サーバーでは動かない)
  • html 要素に lang が付いているか
  • data-pagefind-body を一部のページにだけ付けていないか

インデックスをGitに入れてしまう

dist/ の中に生成されるので、ビルド成果物ごと .gitignore に入っていれば追加の対応は不要です。
生成物をリポジトリに入れると、ビルドのたびに巨大な差分が出るので避けたほうが無難だと思います。

らるじゅらるじゅ

検索は「あって当たり前」なのに、自分で作るとなると意外と重い機能です。
1コマンドで載るなら、迷わず載せてしまっていいと思います。

まとめ|検索はビルドの「後工程」に置くのが正解

Pagefindは、検索機能をフレームワークの中ではなくビルドの後工程に置くという発想のツールです。

Astro側のコードをほとんど汚さずに済むので、あとから入れても、将来別の構成に移っても、そのまま持っていけます。
私が個人ブログでこれを選んでいるのも、この「持ち運べる」性質が大きいです。

この記事のまとめ
  • Pagefindはビルド後のHTMLを読んでインデックスを作る静的サイト向けの全文検索
  • 導入は pagefind を入れて、ビルドコマンドに pagefind --site dist を足すだけ
  • 検索UIは1.5系の Component UI(pagefind-modal などのカスタム要素)が既定。1.4系までの @pagefind/default-ui も引き続き使える
  • 開発サーバーでは動かないので、確認はビルド → プレビューの順で行う
  • 日本語では html 要素の lang が判定材料になるので、lang="ja" を必ず入れておく
  • data-pagefind-body は1ページでも使うと全ページで必要になる点に注意

このブログをWordPressからAstroに移した経緯は、こちらにまとめています。構成ごと見直したい方はあわせてどうぞ。

らるじゅらるじゅ

検索が付くと、自分の書いた記事を自分で探すのがラクになります。
まずは手元のサイトで1回ビルドしてみるところから、ぜひ試してみてください!