search integration with lunr in jekyll
Why Add Search to Jekyll Documentation
As your documentation grows, users may struggle to locate specific pages or keywords using only the navigation menu. By adding a search feature, you improve usability, discovery, and user retention. Since GitHub Pages doesn't support server-side scripts, the solution must run entirely on the client. Lunr.js is a compact search engine built in JavaScript designed for static sites like Jekyll.
What is Lunr.js
Lunr is a JavaScript-based full-text search library that allows you to build an index of your content and search through it directly in the browser. It works offline, doesn’t require server processing, and gives lightning-fast results even for hundreds of pages.
Step 1: Create a Search Index
First, create a JSON index of your content. This file will be consumed by Lunr in the browser. The simplest way is to generate it with a custom Jekyll layout or page.
File: search.json
Add this to your Jekyll project root.
{% raw %}
---
layout: null
permalink: /search.json
---
[
{% assign pages = site.pages | concat: site.posts %}
{% for page in pages %}
{% if page.search != false and page.title %}
{
"title": {{ page.title | jsonify }},
"url": {{ page.url | jsonify }},
"content": {{ page.content | strip_html | strip_newlines | jsonify }}
}{% unless forloop.last %},{% endunless %}
{% endif %}
{% endfor %}
]
{% endraw %}
This builds a single JSON file listing all searchable content on your site. You can customize it to exclude irrelevant pages by checking for page.search != false.
Step 2: Include Lunr.js and Search UI
Now add the Lunr script and a basic UI form to your layout or dedicated search page.
HTML Snippet
<input type="text" id="search-box" placeholder="Search docs..." />
<ul id="search-results"></ul>
<script src="https://unpkg.com/lunr/lunr.js"></script>
<script>
// Load index and set up search
fetch('/search.json')
.then(response => response.json())
.then(data => {
const idx = lunr(function () {
this.ref('url')
this.field('title')
this.field('content')
data.forEach(doc => this.add(doc))
});
document.getElementById('search-box').addEventListener('input', function () {
const query = this.value;
const results = idx.search(query);
const resultList = document.getElementById('search-results');
resultList.innerHTML = '';
results.forEach(result => {
const match = data.find(d => d.url === result.ref);
const li = document.createElement('li');
li.innerHTML = '<a href="' + match.url + '">' + match.title + '</a>';
resultList.appendChild(li);
});
});
});
</script>
Step 3: Styling the Search Experience
To make the search results usable, apply a simple style. You can refine this with your site's existing design system.
#search-box {
padding: 0.5em;
font-size: 1rem;
width: 100%;
max-width: 500px;
box-sizing: border-box;
}
#search-results {
margin-top: 1em;
list-style: none;
padding: 0;
}
#search-results li {
margin-bottom: 0.5em;
}
#search-results a {
text-decoration: none;
color: #3366cc;
}
Step 4: Limit Index Size with Excerpts
To reduce index size and improve speed, you can limit the amount of text added per page by adding a search_excerpt to front matter or programmatically cutting the content.
{% raw %}
"content": {{ page.excerpt | strip_html | jsonify }}
{% endraw %}
You can also define your own excerpt field with:
{% raw %}
{% assign excerpt = page.content | markdownify | strip_html | truncatewords: 30 %}
{% endraw %}
Case Study: Open Source SDK Documentation
A team maintaining documentation for five SDKs hosted their docs on GitHub Pages. Users struggled to find endpoint references and sample code. After implementing Lunr search, the bounce rate dropped by 20% and support tickets asking “where is X documented” fell dramatically.
Implementation Details
- Used a Jekyll collection for endpoints, added search metadata
- Built a
search.jsonwith only relevant fields - Customized result rendering with snippet previews
Feedback Highlights
- "I love that I can type ‘auth’ and find exactly what I need!"
- "No more scrolling through five pages to get to the endpoint"
Security and Performance Considerations
- Ensure your search index doesn’t expose sensitive content
- Compress
search.jsonwith gzip or Brotli - Use search only on pages that need it (set
search: falsein front matter for others) - Split index files if total content exceeds 1MB
Advanced Features
Lunr supports more than just keyword search. You can implement:
- Fuzzy search (partial match)
- Weighted fields (e.g. prioritize title over content)
- Search suggestions and autocomplete
- Stemming and stop word filtering
Conclusion
Integrating Lunr.js with Jekyll adds powerful search functionality to your static documentation, improving user navigation and satisfaction without requiring backend infrastructure. With just a JSON index, a few lines of JavaScript, and thoughtful styling, you can create a responsive search experience that rivals server-powered solutions.
This completes our seven-part guide to building scalable, multilingual, and user-friendly documentation systems using Jekyll and GitHub Pages. From structuring navigation to enabling search, your static site is now fully equipped to serve developers, users, and contributors efficiently.
