Integrate Offline Search into Hugo Static Sites with INFINI Pizza WASM
Live implementations can be seen on the official INFINI Labs website. Simply press the s key to activate the search bar. All queries are processed locally using the embedded WASM module, ensuring instant responses and full functionality even without an internet connection.
Getting Started with Pizza-WASM
The WASM build of INFINI Pizza is available as an open-source project on GitHub: github.com/infinilabs/pizza-wasm. Pre-built artifacts can be downloaded directly from the pkg/ directory.
Here’s the size breakdown of the essential files:
4.0K pizza_wasm.d.ts
4.0K pizza_wasm.js
12K pizza_wasm_bg.js
580K pizza_wasm_bg.wasm
256K pizza_wasm_bg.wasm.gz
After gzip compression, the WASM module is under 260 KB, making it extremely efficient for web delivery. The API surface is minimal—just a few functions to index content and execute searches—perfect for static site use cases.
Simplifying Integration with Pizza-DocSearch
To streamline adoption, INFINI Labs provides pizza-docsearch, a ready-to-use template that bundles the WASM module with a search UI and data schema. It includes example files to help you quickly prototype the integration.
Clone the repository and serve the example locally:
git clone https://github.com/infinilabs/pizza-docsearch.git
cd pizza-docsearch/example/dist
python3 -m http.server 8083
Visit http://localhost:8083 to see the search in action. Notice the index.json file being loaded—it contains structured meatdata for all searchable pages.
Generating Search Data with Hugo
For Hugo-based sites, generate the required JSON index automatically by defining a custom output format. Add this to your site’s config.yaml:
outputs:
home:
- HTML
- RSS
- JSON
Then create a new template file at layouts/index.json with the following content:
{{- $pages := where .Site.RegularPages.ByDate.Reverse "Type" "not in" (slice "page" "json") -}}
{{- $index := slice -}}
{{- range $pages -}}
{{- $content := .Content | markdownify | plainify -}}
{{- $summary := .Params.summary | default .Summary | markdownify | plainify -}}
{{- $tags := .Params.tags | default slice -}}
{{- $category := .Params.category | default "" -}}
{{- $subcategory := .Params.subcategory | default "" -}}
{{- $index = $index | append (dict "title" (.Title | plainify) "url" .Permalink "tags" $tags "category" $category "subcategory" $subcategory "summary" $summary "content" $content) -}}
{{- end -}}
{{- $index | jsonify -}}
Ensure your content files include the required front matter fields:
---
title: "My Blog Post"
category: "Tutorial"
subcategory: "Hugo"
tags: ["search", "static", "wasm"]
summary: "A brief description of the post."
---
Restart your Hugo server. Visit http://localhost:1313/index.json to verify the generated index contains your content.
Embedding the Search UI
From the pizza-docsearch example, copy the folowing three files into your Hugo theme’s assets/ directory:
index-C1z1vz3D.cssindex-D_gOo737.jspizza_wasm_bg-BRCuviY_.wasm
Add the CSS link to your site’s head template (layouts/partials/html-head.html):
<link rel="stylesheet" href="/assets/index-C1z1vz3D.css">
Add the JavaScript loader to your footer template (layouts/partials/html-footer.html):
<script src="/assets/index-D_gOo737.js"></script>
Finally, insert the search container where you want the input field to appear (e.g., in the header):
<div id="docsearch"></div>
After rebuilding your site, the search bar will appear and function entirely offline. No external dependencies, no latency from API calls—just fast, local, and reliable search.
This integration requires only three small files and three code additions. With INFINI Pizza WASM, you can transform any static Hugo site into a fully searchable experience in under five minutes.