enhancing search experience for jekyll knowledge base

Why Search UX Matters in Knowledge Bases

When building a knowledge base, providing effective search functionality is critical. A powerful search engine that is difficult or frustrating to use will drive users away. For Jekyll sites hosted on GitHub Pages, the search is typically client-side, using pre-built JSON indexes. To maximize usability, adding advanced UX features is key.

Core UX Features to Consider

  • Autocomplete suggestions as users type
  • Fuzzy matching to handle typos and partial words
  • Instant result previews without page reloads
  • Keyboard navigation support within results
  • Highlighting matched terms in results

Implementing Autocomplete with Fuse.js

Fuse.js is a lightweight fuzzy-search library perfect for client-side search on static sites. It provides fuzzy matching and supports weighting fields.

Step 1 Setup Fuse.js

Add Fuse.js to your project, either by CDN or local script:


<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/fuse.min.js"></script>

Step 2 Prepare Your Search Index

Your search.json should include fields like title, excerpt, tags, and URL for each page. Example entry:


{
  "title": "How to Use Jekyll Collections",
  "excerpt": "Learn to organize your content with Jekyll collections...",
  "tags": ["jekyll", "collections"],
  "url": "/jekyll-collections"
}

Step 3 Initialize Fuse


const options = {
  keys: ['title', 'excerpt', 'tags'],
  threshold: 0.3,
  includeMatches: true,
  minMatchCharLength: 2
};
let fuse;

fetch('/search.json')
  .then(res => res.json())
  .then(data => {
    fuse = new Fuse(data, options);
  });

Building Autocomplete UI

Create a search input box with a results dropdown container in your layout:


<input type="search" id="search-box" placeholder="Search articles..." autocomplete="off">
<div id="autocomplete-results" role="listbox"></div>

Listening to Input Events


const searchBox = document.getElementById('search-box');
const resultsContainer = document.getElementById('autocomplete-results');

searchBox.addEventListener('input', () => {
  const query = searchBox.value.trim();
  if(query.length < 2) {
    resultsContainer.innerHTML = '';
    return;
  }
  const results = fuse.search(query);
  renderResults(results);
});

function renderResults(results) {
  if(!results.length) {
    resultsContainer.innerHTML = '<p>No results found</p>';
    return;
  }
  resultsContainer.innerHTML = results.map(({ item, matches }) => {
    // Highlight matched terms in title
    let highlightedTitle = item.title;
    if(matches) {
      matches.forEach(match => {
        if(match.key === 'title') {
          match.indices.forEach(([start, end]) => {
            const before = highlightedTitle.substring(0, start);
            const matchText = highlightedTitle.substring(start, end + 1);
            const after = highlightedTitle.substring(end + 1);
            highlightedTitle = `${before}<mark>${matchText}</mark>${after}`;
          });
        }
      });
    }
    return `
      <a href="${item.url}" role="option" tabindex="0">${highlightedTitle}</a>
    `;
  }).join('');
}

Enhancing Accessibility and Keyboard Navigation

Support arrow keys and Enter key to navigate and select results:


let selectedIndex = -1;

searchBox.addEventListener('keydown', e => {
  const options = resultsContainer.querySelectorAll('a');
  if(e.key === 'ArrowDown') {
    selectedIndex = (selectedIndex + 1) % options.length;
    options[selectedIndex].focus();
    e.preventDefault();
  } else if(e.key === 'ArrowUp') {
    selectedIndex = (selectedIndex - 1 + options.length) % options.length;
    options[selectedIndex].focus();
    e.preventDefault();
  } else if(e.key === 'Enter' && selectedIndex > -1) {
    options[selectedIndex].click();
    e.preventDefault();
  }
});

Instant Search Result Previews

Instead of navigating away immediately, you can fetch and show a brief preview snippet from the selected article, improving user confidence.

Approach

  • Use AJAX to fetch the article snippet or excerpt
  • Display it inline below the search result
  • Allow users to click to go to full article

Performance Considerations

  • Limit search index size for very large sites
  • Debounce input to reduce redundant searches
  • Use caching or localStorage for search data
  • Lazy load Fuse.js or search index on demand

Conclusion

Enhancing your Jekyll knowledge base with advanced client-side search features provides users a seamless, fast, and pleasant way to discover content. Combined with previous articles on search indexing and offline caching, this approach creates a modern, user-friendly documentation site on GitHub Pages without backend dependencies.