Jekyll is an excellent static site generator, but it lacks built-in search functionality. Lunr.js is a lightweight client-side search library that allows you to add fast, API-free search to your site.

This guide will walk you through each step, ensuring that by the end, your Jekyll site has a fully functional search feature powered by Lunr.js.

Why Use Lunr.js?

Lunr.js is an easy-to-implement search solution that works entirely on the client-side, meaning:

Lunr.js indexes your content in a JSON format and matches queries in real time.

1. Generate a JSON Search Index

Lunr.js requires structured data for searching, so we first need to create a search.json file that stores all posts or pages in a searchable format.

1. Create json.html

In the _layouts directory, create a file named json.html and write the following code. This extracts each post’s metadata and content into a structured JSON format.

---
layout: null
---
[
  {% for post in site.posts %}
  {
    "title": "{{ post.title }}",
    "url": "{{ post.url }}",
    "content": "{{ post.content | strip_html | truncatewords: 50 }}"
  }
  {% if forloop.last == false %},{% endif %}
  {% endfor %}
]

This will create a JSON file containing:

2. Modify _config.yml to generate search.json

Add the following line to _config.yml. This ensures Jekyll generates the search.json file during site builds.

defaults:
  - scope:
      path: ""
    values:
      layout: "json"

3. Create search.json Output Page

Inside your Jekyll project, create a new page named search.json and add the following line.

---
layout: json
permalink: /search.json
---

After rebuilding the site, search.json will be available at your-site.com/search.json.

Now your Jekyll site has structured data ready for Lunr.js.

2. Install and Configure Lunr.js

Let’s move on to setting up Lunr.js.

1. Include Lunr.js

In default.html (or layout file) include Lunr.js via unpkg CDN:

<script src="https://unpkg.com/lunr/lunr.js"></script>

2. Create search.js for Search Logic

Now we need to fetch search.json, index the data, and process user queries.

In the assets/js/ directory, create a file named search.js and write the following code.

async function fetchSearchData() {
  const response = await fetch("/search.json");  
  const data = await response.json();

  const idx = lunr(function () {
    this.field('title');   // Index title
    this.field('content'); // Index content
    this.ref('url');       // Use URL as reference

    data.forEach(doc => this.add(doc));
  });

  document.querySelector("#search-input").addEventListener("input", function () {
    const results = idx.search(this.value);
    displayResults(results, data);
  });
}

function displayResults(results, data) {
  const resultContainer = document.querySelector("#search-results");
  resultContainer.innerHTML = "";

  results.forEach(result => {
    const item = data.find(doc => doc.url === result.ref);
    resultContainer.innerHTML += `<li><a href="${item.url}">${item.title}</a></li>`;
  });
}

fetchSearchData();

Your JavaScript script is ready to handle search queries.

3. Add a Search Box to Your Site

Modify your Jekyll HTML template to include a search box and results area.

1. Edit default.html (or layout file)

This sample code adds a search field, a list to display search results, and links to Lunr.js and search.js.

<input type="text" id="search-input" placeholder="Search..." />
<ul id="search-results"></ul>

Don’t forget to include search.js in HTML file, too.

<script src="{{ '/assets/js/search.js' | absolute_url }}" defer></script>

4. Customize the Search Results

Right now, search results display as basic links, but you can customize how results appear.

Improving Results Display

Modify displayResults() inside search.js to add a preview snippet of the content. It shows “No results found” if no matches found.

function displayResults(results, data) {
  const resultContainer = document.querySelector("#search-results");
  const searchInput = document.querySelector("#search-input").value.trim();

  // Clear and hide search results if input is empty
  if (searchInput === "") {
    resultContainer.innerHTML = "";
    return;
  }

  resultContainer.innerHTML = "";

  if (results.length === 0) {
    resultContainer.innerHTML = "<li>No results found</li>";
    return;
  }

  results.forEach(result => {
    const item = data.find(doc => doc.url === result.ref);
    resultContainer.innerHTML += `
    <li>
      <a href="${item.url}">
      <strong>${item.title}</strong>
      </a>
      <p>${item.content.substring(0, 100)}...</p>
    </li>`;
  });
}

5. Test and Optimize the Search Function

Your Jekyll site now has fully functional search powered by Lunr.js.

Alternative Search Methods

If Lunr.js doesn’t fit your needs, consider these options.

1. Netlify Functions (Server-Side Search)

2. Google Custom Search Engine

Each method has advantages so choose the best one for your site’s needs.

In Closing

Lunr.js is a fantastic lightweight solution for adding search to Jekyll sites. No backend required and simple to implement. It also has easy customization options.

Now that you have detailed steps, how does it feel working with Lunr.js for the first time? Excited to test it on your site? Let me know if you need any tweaks before publishing.