[Astro] Pagefindを利用した検索実装

2026-04-01 hit count image

AstroブログにPagefindを利用した静的検索機能を実装する方法を共有します。ビルド統合、多言語検索、カテゴリフィルタリングなどを扱います。

astro

概要

前回の記事広告連携(Google AdSense)で広告連携を扱いました。今回の記事では静的サイトでサーバーなしに検索機能を実装する方法を説明します。

Pagefindとは

Pagefindは静的サイトのための検索ライブラリです。ビルドされたHTMLファイルを分析して検索インデックスを生成し、クライアントサイドで検索を実行します。

主な特徴

他の検索ソリューション(Algolia、Elasticsearchなど)と異なり、別途サーバーなしに静的サイトだけで動作するのがPagefindの最大の利点です。

  • サーバー不要:ビルド時にインデックスを生成して静的ファイルとして提供
  • 軽量:検索インデックスが小さくJavaScriptバンドルも軽い
  • 高速検索:WebAssemblyベースでブラウザで高速に動作
  • 多言語対応:言語別インデキシングが可能

インストール

Pagefindはビルド時にのみ使用するため、開発依存パッケージ(-D)としてインストールします。

npm install -D pagefind

ビルド統合

package.jsonbuildスクリプトでAstroビルド後にPagefindインデキシングを実行します。

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

Pagefinddist/ディレクトリのHTMLファイルを分析してdist/pagefind/ディレクトリに検索インデックスを生成します。そのため必ずastro buildの後に実行する必要があります。

検索対象の指定

Pagefindはデフォルトでページのすべてのテキストをインデキシングします。しかしナビゲーション、フッターなど本文以外の領域まで検索されると不必要な結果が表示される可能性があります。data-pagefind-body属性を使用すると検索対象を本文領域に限定できます。

<!-- src/layouts/PostLayout.astro -->
<article data-pagefind-body>
  <slot />
  <!-- マークダウンコンテンツ -->
</article>

検索から除外する要素にはdata-pagefind-ignore属性を使用します。

メタデータフィルター

検索結果をカテゴリ別にフィルタリングするにはdata-pagefind-filter属性でメタデータを指定します。この属性が含まれた要素のテキストがフィルター値として使用されます。

<!-- src/layouts/PostLayout.astro -->
<span data-pagefind-filter="category">{category}</span>

検索ページの実装

検索機能はSearchBar.astro(ホームページ検索バー)とSearchPage.astro(専用検索ページ)で実装されています。

SearchPage.astro主要構造

検索ページはカテゴリフィルターチップ、検索ステータス表示、検索結果リスト、ページネーションで構成されています。サーバーサイドでカテゴリ一覧と翻訳データを準備し、実際の検索はクライアントサイドJavaScriptで処理します。

---
// src/components/SearchPage.astro
import { useTranslations, getLocalizedPath } from '../i18n/translations';
import { getMainCategories } from '../data/categories';

const { lang } = Astro.props;
const t = useTranslations(lang);
const mainCategories = getMainCategories();
---

<div class="search-page">
  <!-- カテゴリフィルターチップ -->
  <div class="search-page__filters" id="search-filters">
    <button class="search-page__chip active" data-category="">
      {t('search.allCategories')}
    </button>
    {categoryChips.map((chip) => (
      <button class="search-page__chip" data-category={chip.slug}>
        {chip.title}
      </button>
    ))}
  </div>

  <!-- 検索ステータス -->
  <div class="search-page__status" id="search-status"></div>

  <!-- 検索結果 -->
  <div class="search-page__results" id="search-results"></div>

  <!-- ページネーション -->
  <nav id="search-pagination" class="search-page__pagination"></nav>
</div>

クライアントサイド検索ロジック

実際の検索はブラウザで実行されます。PagefindのJavaScript APIを使用して検索キーワードを入力するとビルド時に生成されたインデックスから結果を探して返します。

// src/components/SearchPage.astro 内クライアントスクリプト
const pagefind = await import('/pagefind/pagefind.js');
await pagefind.init();

// 検索実行
const search = await pagefind.search(query);

// 結果ロード
const results = await Promise.all(search.results.map((r) => r.data()));

URLクエリパラメータ(?q=検索キーワード)を使用して検索キーワードを維持するため、検索結果ページをブックマークしたり共有したりできます。

多言語検索

このブログは日本語、韓国語、英語をサポートしているため、各言語別に別々の検索ページを提供しています。

言語検索ページURL
日本語/search/
韓国語/ko/search/
英語/en/search/

Pagefindのインデックスにはすべての言語のコンテンツが含まれますが、検索時に該当言語の結果のみフィルタリングして表示します。

カテゴリフィルタリング

検索結果が多い時に見たいカテゴリだけを選べると便利です。categories.tsに定義されたカテゴリ一覧をチップボタンで表示し、ユーザーが選択したカテゴリで結果をフィルタリングします。

// src/components/SearchPage.astro 内クライアントスクリプト
const search = await pagefind.search(query, {
  filters: {
    category: selectedCategory,
  },
});

Jekyll検索との比較

現在のブログはJekyllで運営していたものをAstroにマイグレーション中です。Jekyll検索実装については検索バー作成ポストを参考にしてください。

JekyllとAstro(Pagefind)の検索実装の主な違いは以下の通りです。

項目JekyllAstro(Pagefind)
検索方式JSONファイルベースのクライアント検索WebAssemblyベースのインデックス検索
インデックスサイズ全コンテンツをJSONで生成(サイズ大)最適化されたバイナリインデックス(小さい)
検索速度JavaScript文字列マッチングWebAssemblyで高速検索
設定の複雑さカスタムJSON生成 + 検索ロジックが必要npx pagefind一行でインデックス生成
カテゴリフィルター自前実装が必要data-pagefind-filter属性で簡単に実装

完了

今回の記事ではPagefindを利用した静的検索の実装を見てきました。

  • Pagefindでサーバーなしにクライアントサイド検索を実装
  • astro build && npx pagefind --site distでビルドパイプラインに統合
  • data-pagefind-bodydata-pagefind-filterで検索対象とフィルターを指定
  • 言語別検索ページで多言語検索をサポート
  • カテゴリチップボタンで結果をフィルタリング

次の記事レイアウトとコンポーネントアーキテクチャではブログのレイアウトとコンポーネント構造を扱います。

シリーズ案内

このポストはJekyllからAstroへのマイグレーションシリーズの一部です。

  1. JekyllからAstroへマイグレーションした理由
  2. Astroのインストールとプロジェクト構成
  3. Content Collectionsとマークダウンマイグレーション
  4. 多言語(i18n)実装
  5. SEO実装
  6. 画像最適化 — カスタムrehypeプラグイン
  7. コメントシステム(Utterances)
  8. 広告連携(Google AdSense)
  9. Pagefindを利用した検索実装
  10. レイアウトとコンポーネントアーキテクチャ
  11. GitHub Pagesデプロイ
  12. ソーシャル共有自動化スクリプト
  13. トラブルシューティングとヒント

私のブログが役に立ちましたか?下にコメントを残してください。それは私にとって大きな大きな力になります!

アプリ広報

今見てるブログを作成たDekuが開発したアプリを使ってみてください。
Dekuが開発したアプリはFlutterで開発されています。

興味がある方はアプリをダウンロードしてアプリを使ってくれると本当に助かります。



SHARE
Twitter Facebook RSS