Development & Building with AI · Tutorial
How to add site search to an Astro website
A static site has no server to answer searches. Pagefind works around this by generating an index after the build, which the browser itself queries. Here is how to set up search in an Astro project.
- Author:
- Redação Token & Tech
- Responsible editor:
- Daniel Ferreira
- Translation:
- Redação Token & Tech
Published · 9 min read
Originally published in Brazilian Portuguese.
Translated from the Portuguese original with AI assistance.
Also available in: Português
Key points
- Pagefind runs after astro build, reads the HTML generated in dist/ and writes a chunked static index, with no search server.
- Attributes such as data-pagefind-body and data-pagefind-ignore define what goes into the index; the lang attribute keeps a separate index per language.
- Search only appears after a full build and only reflects the content of the last build; it does not work on the development server.
On a site with a few pages, the menu is enough. Once the archive grows past dozens of articles, readers need to type a word and find what they are looking for. On a static site, generated as HTML files, that is a problem: there is no database or server to answer the query.
This tutorial shows how to solve it in an Astro project, the framework that generates sites from components and content files, using Pagefind, a search library for static sites. The instructions follow the official documentation of both tools, consulted on October 8, 2026. At the end, the article describes how Token & Tech’s own search is set up.
How Pagefind works
Pagefind does not read your source code. According to the quick start documentation, it runs after the site generator, reads the finished HTML files and writes a static search bundle into the same folder: the index and the interface files.
Three practical consequences follow:
- there is no search server. The documentation says Pagefind has no server component; search is embedded in the static site and runs in the visitor’s browser;
- the index is split into chunks. A search downloads only the part of the index it needs, not the whole index;
- it works with any generator, because it depends only on the final HTML. Astro, Hugo or hand-written pages are all treated the same way.
On the project’s home page, the authors state that a full-text search on a 10,000-page site transfers less than 300 kB over the network, including the library itself, and that for most sites the figure is close to 100 kB. These are figures published by the project itself, with no methodology published on the page.
The current version, as of this check, is 1.5.2, released April 12, 2026, according to the releases page on GitHub. Version 1.5.0, from April 6, 2026, introduced a new interface based on web components, which you will see in step 2.
Before you start
You need an Astro project that generates static files. According to Astro’s configuration reference, that is the default behavior: the output option is 'static' and the astro build command writes the result to ./dist. If you changed outDir, replace dist with your directory in the commands below.
You also need Node.js installed, since Pagefind is distributed as an npm package (there is also installation via Python, a prebuilt binary or compiling from Rust, according to the documentation).
Step 1: chain Pagefind to the build
The quick start documentation shows the basic command:
npx -y pagefind --site public --serve
The --site option points to the folder generated by the site. The example uses public because it was written for Hugo; in Astro, the folder is dist. The --serve option starts a preview server and is only for testing.
In production, the recommendation is to run Pagefind after every build, before publishing. In an Astro project, the simplest way is to install the package as a development dependency and chain the commands in package.json:
npm install --save-dev pagefind
{
"scripts": {
"build": "astro build && pagefind --site dist"
}
}
Order matters. If Pagefind runs before astro build, there will be no HTML to index. When it finishes, it reports how many pages and words it indexed; check that summary in the build log.
Step 2: create the search interface
Pagefind generates the interface files inside dist/pagefind/. They only exist after the build, and that affects how you reference them in Astro.
Since version 1.5.0, the recommended interface is the Component UI, made of custom HTML elements. The Component UI guide includes this minimal example, which opens a search overlay:
---
// src/components/Search.astro
---
<link href="/pagefind/pagefind-component-ui.css" rel="stylesheet" />
<script is:inline src="/pagefind/pagefind-component-ui.js" type="module"></script>
<pagefind-modal-trigger></pagefind-modal-trigger>
<pagefind-modal></pagefind-modal>
There is a reason for is:inline. According to Astro’s scripts guide, this directive tells Astro not to process or bundle the script, which is the case for files served as-is, from public/ or from outside the project. The Pagefind file does not exist when Astro compiles, so Astro should not try to process it.
According to the Component UI guide, the default shortcut to open the overlay is Ctrl+K (Cmd+K on Mac), and Esc closes it. There are other components: <pagefind-searchbox> for a compact box in the menu, and <pagefind-input> with <pagefind-results> to build a dedicated search page.
The previous interface, called the Default UI, is still documented. It uses pagefind-ui.css, pagefind-ui.js and a JavaScript constructor:
<div id="search"></div>
<script is:inline src="/pagefind/pagefind-ui.js"></script>
<script is:inline>
window.addEventListener("DOMContentLoaded", () => {
new PagefindUI({ element: "#search", showSubResults: true });
});
</script>
The Default UI documentation says the Component UI replaces it, but does not mark the old one as deprecated or give an end-of-support date. The 1.5.0 release notes say it remains supported “for now”. For a new project, the Component UI is the path the documentation points to; for an existing project, the switch can wait.
The Default UI options are in the interface configuration reference. Two deserve attention: showImages, which displays an image in each result and is on by default, and autofocus, which puts the cursor in the field on load and, according to the documentation itself, can cause accessibility problems if used carelessly.
Step 3: decide what goes into the index
Without configuration, Pagefind starts indexing from the body tag of each page. According to the page on what gets indexed, it already skips some elements on its own, such as nav, footer, script and form. Everything else goes in, including headers, sign-up boxes and “read also” lists that repeat on every page and clutter the results. The same page describes the attributes that control this behavior.
data-pagefind-body marks the main area of the page. There is an effect that catches many people off guard: once at least one page on the site has this attribute, pages without it are no longer indexed. If you put it only in the article layout, the home page disappears from search.
data-pagefind-ignore excludes an element and its children from the index. With the default value, Pagefind still reads metadata and filters inside it. With data-pagefind-ignore="all", nothing from that section is used.
In an Astro layout, the typical combination looks like this (marking nav and footer is redundant, but makes the intent explicit):
<header data-pagefind-ignore>...</header>
<main data-pagefind-body>
<nav aria-label="Breadcrumb" data-pagefind-ignore>...</nav>
<slot />
</main>
<footer data-pagefind-ignore>...</footer>
Each result’s title and image also come from the HTML. According to the metadata page, Pagefind uses the first h1 as the title and the first image after it as the thumbnail. You can override this with data-pagefind-meta, for example data-pagefind-meta="title" on another element. The same page states that each metadata key holds only one value per page and that matches in the title get five times more weight in ranking, by default.
Step 4: separate languages
If the site has more than one language, the lang attribute on the html tag does the work. According to the multilingual search documentation, Pagefind reads this attribute when indexing and creates one index per language. In the browser, it reads the same attribute and loads the corresponding index. A page in pt-br searches only among pages in pt-br.
The same page explains two details:
- Pagefind applies stemming, reducing words to their root, when the language is supported; without support, “search” and “searching” may not match;
- the interface is translated into several languages and adapts to the page’s language; when there is no translation, it appears in English.
There is an option to force a single language, which turns off this behavior and generates one index for the whole site. If you already have per-language routes in Astro, as described in How to build an international site with Astro, just make sure each layout writes the correct lang.
Step 5: filters, if they make sense
Filters let you narrow results by category, author or another field. According to the filtering page, you mark the value in the HTML:
<span data-pagefind-filter="category">{category}</span>
<meta data-pagefind-filter="author[content]" content={author} />
The first form uses the element’s text; the second reads an attribute. The filter attribute works even outside the area marked with data-pagefind-body and inside ignored sections. The keys any, all, none and not are reserved.
Filters are only worth it if readers really need them. On a small archive, a simple search box is usually enough.
Step 6: test
The most common mistake is testing in the wrong place. Pagefind’s documentation warns that the development server does not show search, because the index only exists after the build. In Astro, that means astro dev is not a valid test.
The workflow that works:
- run
npm run buildand check the number of indexed pages in the log; - run
astro preview, which, according to the command-line reference, serves the contents ofdist/locally; - search for terms you know exist, terms with accents and terms from pages that should be left out.
If an expected page does not show up, check data-pagefind-body first. If menus appear in result excerpts, a block is missing data-pagefind-ignore.
What if the site uses Starlight
Starlight, the documentation theme maintained by the Astro team, comes with Pagefind already configured. According to Starlight’s search page, you just build and publish. To remove a page from the index, use pagefind: false in the file’s frontmatter; to remove just a section, data-pagefind-ignore. The theme also allows replacing Pagefind with Algolia DocSearch, a hosted search service, through an official plugin.
The Token & Tech case
This site is built with Astro and uses Pagefind. The description below comes from the project’s code, not from performance testing.
- Build. The build script runs
astro buildand thenpagefind --site dist, before a custom post-processing step. Pagefind is installed as a development dependency, at version 1.5.2. - What goes in. The base layout applies
data-pagefind-bodytomainonly on pages marked for indexing by search engines and that are not the home page. Pages withnoindexare left out of site search by the same criterion. - What stays out. The header, breadcrumb, credits and dates block, review notices, sign-up box and related articles list carry
data-pagefind-ignore. - Languages. Each page declares its language in the
htmltag’slang, which produces a separate index per language. - Interface. The search page uses the Default UI, with results by subheading (
showSubResults), no images and autofocus on the field. It also reads theqparameter from the URL to open with a search already filled in, and shows a notice for visitors without JavaScript.
In our newsroom’s assessment, migrating to the Component UI is a possible improvement, not an urgent one, since the current interface remains documented.
Limits you need to know
- The index ages. It reflects the last build. New content only appears in search after a new build and a new deployment.
- It depends on JavaScript. Without JavaScript, there is no search. It is worth showing a notice in
<noscript>and keeping category navigation working. - Text search, not semantic search. Pagefind finds words. It does not understand synonyms or natural-language questions; for that there are approaches like the one described in RAG: how to connect artificial intelligence to documents, with other costs and other infrastructure.
- Actual size. The transfer figures cited above come from the project itself. Measure on your own site, using the browser’s network tab, before promising performance to anyone.
Key takeaways
Pagefind fits Astro’s model well because it works on the final output: you generate the site, index the dist/ folder and publish everything together. The part that requires attention is not the installation, which fits in one line of package.json, but curating what goes into the index, with data-pagefind-body, data-pagefind-ignore and the correct lang. Always test on a full build, and remember that search only knows what was on the site at the time of the last build.
Disclosure: The Token & Tech website uses Pagefind for its site search.
Sources
- Getting Started with Pagefind, Pagefind. Accessed October 8, 2026
- Pagefind, Pagefind. Accessed October 8, 2026
- Configuring what content is indexed, Pagefind. Accessed October 8, 2026
- Setting up metadata, Pagefind. Accessed October 8, 2026
- Setting up filters, Pagefind. Accessed October 8, 2026
- Multilingual search, Pagefind. Accessed October 8, 2026
- Using the Default UI, Pagefind. Accessed October 8, 2026
- Pagefind UI configuration options, Pagefind. Accessed October 8, 2026
- Pagefind Component UI (guia para LLMs), Pagefind. Accessed October 8, 2026
- Releases · Pagefind/pagefind, Pagefind (GitHub). Published April 6, 2026 · Accessed October 8, 2026
- Configuration Reference, Astro Docs. Accessed October 8, 2026
- Scripts and event handling, Astro Docs. Accessed October 8, 2026
- CLI Commands, Astro Docs. Accessed October 8, 2026
- Site Search, Starlight (Astro). Accessed October 8, 2026