Pagefind в Astro: пошаговая настройка поиска
Поиск по контентному сайту — типичная задача, у которой почему-то нет «канонического» решения. Я перепробовал три подхода: 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, обычно это занимает день и даёт ощутимо более стильный результат, чем дефолтный компонент.