lenec ru

← все посты

Pagefind в Astro: пошаговая настройка поиска

14K

Поиск по контентному сайту — типичная задача, у которой почему-то нет «канонического» решения. Я перепробовал три подхода: Algolia, Lunr и Pagefind. К 2026 году именно Pagefind стал у меня дефолтом для Astro-проектов и вообще любых статических сайтов. Делюсь рабочей конфигурацией и подводными камнями.

Что такое Pagefind в одной фразе: статический поиск, который индексирует HTML после билда и кладёт рядом с сайтом мини-WASM-модуль с UI-компонентом. Никакого сервера, никакой подписки, никаких ключей.

Что нужно знать перед тем, как ставить

Pagefind работает с уже собранным HTML — его поведение не зависит от фреймворка. Главное условие: контент должен реально быть в HTML на момент билда. Для статических страниц Astro это естественно. Для SSR-страниц Pagefind не подойдёт — индексировать нечего, страница рендерится в рантайме.

Если у тебя гибридный проект, можно индексировать только статическую часть, проставив prerender = true для постов и оставив остальное динамическим.

Установка

Ставлю как обычный пакет:

pnpm add -D pagefind

Дальше в package.json добавляю шаг индексирования после билда:

{
  "scripts": {
    "build": "astro build && pagefind --site dist",
    "start": "node ./dist/server/entry.mjs"
  }
}

На выходе появится папка dist/pagefind с индексом, шрифтами и UI-компонентами. На статическом сайте этого достаточно. На гибридном надо проследить, чтобы папка попала в раздачу адаптера или в CDN.

Поисковая страница

Создаю src/pages/search.astro и подключаю UI Pagefind:

---
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title="Поиск">
  <link href="/pagefind/pagefind-ui.css" rel="stylesheet" />
  <script src="/pagefind/pagefind-ui.js"></script>
  <div id="search"></div>
  <script>
    window.addEventListener('DOMContentLoaded', () => {
      new PagefindUI({
        element: '#search',
        showSubResults: true,
        translations: {
          placeholder: 'Найти...',
          clear_search: 'Очистить',
          load_more: 'Показать ещё',
        },
      });
    });
  </script>
</BaseLayout>

Это даст работающий поиск из коробки. UI Pagefind простой, но настраиваемый: можно переопределить классы, дописать стили, выкинуть результаты определённых разделов.

Чем индексировать

По умолчанию Pagefind берёт <main>, если он есть, иначе <body>. Если у тебя в макете есть боковая панель или подвал, которые не должны попадать в индекс — оборачивай контент в <main data-pagefind-body> или просто в <main>.

Игнорировать отдельные блоки — атрибутом data-pagefind-ignore:

<aside data-pagefind-ignore>
  <p>Этот блок не попадёт в поиск.</p>
</aside>

Кастомные веса для заголовков задаются классами Pagefind — поищи в документации, тема большая, для блога обычно хватает дефолтов.

Метаданные и фильтры

Pagefind умеет работать с фильтрами — например, по тегам или авторам. На странице нужно добавить разметку с атрибутами:

<article>
  <h1>Заголовок поста</h1>
  <p data-pagefind-meta="author">Никита Воробьёв</p>
  <p data-pagefind-filter="section:Astro">Astro</p>
  <p>Содержимое поста...</p>
</article>

Дальше в UI можно включить showSubResults и resetStyles, и Pagefind сам нарисует фильтры.

Кастомный API

Если стандартный UI не подходит, можно работать с Pagefind напрямую через JS:

const pagefind = await import('/pagefind/pagefind.js');
await pagefind.options({ baseUrl: '/' });

const search = await pagefind.search('react server components');
for (const r of search.results) {
  const data = await r.data();
  console.log(data.url, data.excerpt);
}

Это полезно, когда хочется свою стилизацию или специальные сценарии — поиск с подсказками, поиск только по разделам, поиск с подсветкой в реальном тексте.

Как Pagefind ведёт себя на больших сайтах

На блоге в 600 страниц итоговый индекс — около 2 МБ, разбитый на чанки по 100–200 КБ. Каждый чанк подгружается лениво, когда юзер начинает печатать. На стартовой странице запроса вообще ничего не загружается — только небольшой загрузчик и шрифты, если ты их используешь.

Я тестировал на сайте в 4 200 страниц: общий индекс 14 МБ, билд индексации — 38 секунд. По сравнению с Lunr это почти ничто, особенно если у тебя CDN перед сайтом.

Локализация

Pagefind умеет в многоязычные сайты. Если у тебя страницы на нескольких языках, оборачивай корень в <html lang="ru"> и/или <main lang="ru"> — Pagefind разделит индекс по языкам и будет применять подходящие стеммеры.

Русский поддерживается из коробки. Из мелочей: лучше держать тексты в реальном HTML, а не подгружать через JS — иначе индексатор не увидит контент.

Что я бы сразу настроил

  • Подключение CSS и JS Pagefind через <link>/<script> с атрибутом defer, чтобы не блокировать первую отрисовку.
  • Шрифты Pagefind — отдать с font-display: swap, иначе на медленной сети будет «прыгать» инпут.
  • Тёмная тема: Pagefind использует переменные CSS, переопредели их под свой дизайн (или подставь свои классы).
  • Игноры на навигацию, подвал и боковые панели — иначе сниппеты будут заполнены меню.

Подводные камни

Самый частый — pagefind не запускается, потому что папка dist не существует на момент команды. Это случается, когда ставишь индексацию в скрипт postbuild, а собственно сборку делаешь не через build. Запускай явно: сначала astro build, потом pagefind --site dist.

Второй — кэш CDN. Pagefind кладёт версии индексов в файлы с хешем. Если у тебя CDN отдаёт старый pagefind.js, он попытается подгрузить чанки по старым именам. Решение — инвалидация по всему пути /pagefind/* после деплоя.

Что копать дальше

Pagefind работает не только с Astro. Я встраивал его в Hugo, Eleventy и в собственный билд на Vite — везде сценарий одинаковый. Если ты настроил поиск в одном проекте, во второй переедешь за пятнадцать минут. Следующий шаг — отрисовать собственный UI поиска поверх API, обычно это занимает день и даёт ощутимо более стильный результат, чем дефолтный компонент.

Комментарии 0

  • Будьте первым, кто оставит комментарий.

Войдите, чтобы оставить комментарий.