DEV Community

Cover image for Moving Away from Google Site Search: Implementing Pagefind for Static Sites
tumf
tumf

Posted on • Originally published at blog.tumf.dev

Moving Away from Google Site Search: Implementing Pagefind for Static Sites

Originally published on 2026-02-02
Original article (Japanese): Googleのsite:検索をやめて: 静的サイトにPagefindを導入

Moving Away from Google Site Search: Implementing Pagefind for Static Sites

I have transitioned the site search for this blog from Google Search (site:) to Pagefind.

After building the public/ directory with Hugo, I will index it with Pagefind, allowing the search to be completed entirely within the static site.

Why Google Site Search Has Become Unreliable

This blog is a static site generated with Hugo and hosted on Cloudflare Pages. Previously, I had a Google search form in the navigation bar, using site:blog.tumf.dev to perform site searches.

<form action="https://www.google.com/search" method="get">
  <input type="search" name="q" placeholder="Search" />
  <input type="hidden" name="as_sitesearch" value="blog.tumf.dev" />
</form>
Enter fullscreen mode Exit fullscreen mode

This setup assumes that "Google is indexing its own site." If the indexing status changes, it can lead to empty search results.

Options for Static Site Search

After researching search methods that do not rely on Google, I found the following main options:

Method Features Japanese Support Cost
Algolia High functionality and speed ✅ Good Paid (with free tier)
Lunr.js Lightweight, client-side search ⚠️ Weak Free
Fuse.js Fuzzy search ✅ Good Free
Pagefind Static, low bandwidth ✅ Supported Free

I wanted to avoid the paid option of Algolia for a personal blog. Lunr.js has weak tokenization for Japanese. Fuse.js seemed promising, but there are few integration examples with Hugo.

Ultimately, I chose Pagefind for the following reasons:

  1. Completely Static - No server required, works well with Cloudflare Pages
  2. Low Bandwidth - Designed to read only the necessary chunks during search
  3. Japanese Support - Handles tokenization for CJK languages
  4. Many Implementation Examples - Numerous examples of adoption in static sites, making information easy to find

How Pagefind Works

Pagefind takes an approach of "generating an index after building."

# 1. Build the site with Hugo
hugo

# 2. Generate the index with Pagefind
npx -y pagefind --site public
Enter fullscreen mode Exit fullscreen mode

The generated index is placed in the public/pagefind/ directory and is loaded via JavaScript during searches.

Optimizing Index Size

A key feature of Pagefind is that it "loads only the necessary parts during search." The full-text search index is divided into multiple chunks, and only the required chunks are downloaded based on the search query.

In the case of this blog (about 135 articles):

Indexed 135 pages
Indexed 10,376 words
Created 27 index chunks
Enter fullscreen mode Exit fullscreen mode

The index is split into 27 chunks, so only the necessary chunks are loaded during searches, eliminating the need to download the entire index at once.

Implementation Steps

1. Create package.json (Optional)

Since Pagefind is provided as an npm package, it's convenient to include it in package.json for ongoing maintenance. If you prefer to run it with npx each time, you can skip this step.

{
  "name": "blog-tumf-dev",
  "scripts": {
    "build": "hugo && npx pagefind --site public",
    "serve": "hugo server -D"
  },
  "devDependencies": {
    "pagefind": "^1.4.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Add data Attributes to HTML Elements

Pagefind specifies search targets with the data-pagefind-body attribute. Modify the article template (layouts/_default/single.html):

<article class="hentry" role="article" data-pagefind-body>
  <!-- Article content -->
</article>
Enter fullscreen mode Exit fullscreen mode

Add data-pagefind-meta="title" to the article title (layouts/partials/post_header.html):

<h1 class="entry-title" data-pagefind-meta="title">
  <a href="{{ .Permalink }}">{{ .Title }}</a>
</h1>
Enter fullscreen mode Exit fullscreen mode

3. Add Search UI

Pagefind provides a pre-built search UI. Add a search box to the navigation bar (layouts/partials/pagefind-search.html):

<form class="pagefind-form">
  <fieldset role="search">
    <div id="pagefind-search"></div>
  </fieldset>
</form>
<link href="/pagefind/pagefind-ui.css" rel="stylesheet">
<script src="/pagefind/pagefind-ui.js"></script>
<script>
  window.addEventListener('DOMContentLoaded', function() {
    new PagefindUI({
      element: "#pagefind-search",
      showSubResults: false,
      showImages: false,
      excerptLength: 20,
      translations: {
        placeholder: "Search",
        zero_results: "No articles match ‘[SEARCH_TERM]’"
      }
    });
  });
</script>
Enter fullscreen mode Exit fullscreen mode

4. Limit Indexed Targets

By default, all HTML files are indexed, but I want to exclude drafts and listing pages, so I configure this in pagefind.yml:

site: public
glob: "posts/**/*.html"
Enter fullscreen mode Exit fullscreen mode

This ensures that only articles under posts/ are indexed.

5. Exclude Future-Dated Articles

Hugo does not build future-dated or draft articles by default, but if you are including them in your CI/CD setup or build flags, it’s wise to explicitly disable them.

buildFuture = false
buildDrafts = false
Enter fullscreen mode Exit fullscreen mode

6. Cloudflare Pages Build Settings

Change the build command for Cloudflare Pages to:

npm run build
Enter fullscreen mode Exit fullscreen mode

or

hugo && npx -y pagefind --site public
Enter fullscreen mode Exit fullscreen mode

Limitations and Countermeasures for Japanese Search

Pagefind supports Japanese tokenization, but there are some limitations.

Limitation 1: No Stemming Support

In English, searching for "running" will also hit "run," but in Japanese, you need to search for "走る," "走った," and "走っている" individually.

Limitation 2: Some Search Terms May Require Workarounds

Since tokenization occurs during indexing, using space-separated terms may improve hit rates for certain search queries.

Countermeasure: Search Hints

The placeholder for the search box is set to "Search," indicating that searches can be performed in both Japanese and English. Additionally, if there are zero results, you might consider displaying a hint like "Try separating terms with spaces."

Design Adjustments

The Pagefind UI has a modern design by default, but I customized the CSS to match the existing theme.

Following the Style of the Original Google Search Form

In the original theme (Octopress), the search box had the following style:

body > nav form .search {
  padding: 0.3em 0.5em 0;
  font-size: 0.85em;
  border-radius: 0.5em;
  background-color: #f2f2f2;
  border: 1px solid #b3b3b3;
}
Enter fullscreen mode Exit fullscreen mode

I overrode the Pagefind UI styles to match this appearance:

.pagefind-ui .pagefind-ui__search-input {
  padding: 0.3em 0.5em !important;
  font-size: 0.85em !important;
  border-radius: 0.5em !important;
  background-color: #f2f2f2 !important;
  border: 1px solid #b3b3b3 !important;
}
Enter fullscreen mode Exit fullscreen mode

Dropdown for Search Results

I configured the search results to display in a dropdown below the navigation bar:

.pagefind-ui .pagefind-ui__drawer {
  position: absolute !important;
  top: 100% !important;
  right: 0 !important;
  z-index: 1000 !important;
  background: #fff !important;
  border: 1px solid #ddd !important;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
  max-height: 400px !important;
  overflow-y: auto !important;
  width: 350px !important;
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

The transition from Google Search (site:) to Pagefind was smoother than I expected.

Pros:

  • Completely static, so it does not depend on Google’s indexing status
  • Low bandwidth and fast (loads only the necessary chunks during search)
  • Practical level of Japanese search support
  • Easy design customization

Cautions:

  • No stemming support for Japanese
  • Some search terms may benefit from being space-separated
  • Index generation is required during build (slightly increases build time)

For those looking to implement "search without relying on Google" for personal blogs or technical documentation sites, I believe Pagefind is a great choice.

Reference Links

Top comments (0)