personalize search results using client side logic in jekyll
Understanding the Value of Personalization
Personalized search experiences help users find what they need faster by tailoring results based on their preferences or history. In a static Jekyll knowledge base hosted on GitHub Pages, where there’s no backend, this personalization can still be achieved using JavaScript and browser-based storage like localStorage.
Step 1: Define Personalization Goals
Before implementing anything, you need to define what kind of personalization you want to offer. Common strategies include:
- Remembering previous search terms and prioritizing results related to them.
- Highlighting frequently visited content or content from previously visited categories.
- Language preference detection and filtering results accordingly.
Step 2: Store User Preferences Locally
Use localStorage to persist user preferences between sessions. For example, when a user searches for “setup,” you can store that interaction like this:
const recentSearches = JSON.parse(localStorage.getItem('searchHistory')) || [];
recentSearches.unshift('setup');
localStorage.setItem('searchHistory', JSON.stringify(recentSearches.slice(0, 10)));
This records the last 10 search queries a user has made, which can be used to prioritize future results.
Step 3: Modify Search Ranking Based on History
Once you have stored history, enhance your search function to rank results based on past interactions.
function rankResults(results, history) {
return results.sort((a, b) => {
const aScore = history.some(term => a.title.includes(term)) ? 1 : 0;
const bScore = history.some(term => b.title.includes(term)) ? 1 : 0;
return bScore - aScore;
});
}
This simple ranking model bumps up results that contain previously searched terms.
Step 4: Categorize User Preferences
You can also store and track preferred categories based on the content users read most often.
const currentCategory = document.querySelector('meta[name="category"]').content;
const categoryCount = JSON.parse(localStorage.getItem('categoryCount')) || {};
categoryCount[currentCategory] = (categoryCount[currentCategory] || 0) + 1;
localStorage.setItem('categoryCount', JSON.stringify(categoryCount));
Later, when rendering search results, you can favor results from these more visited categories.
Step 5: Adjust the Search Interface Based on Preferences
With enough usage data, you can even tweak the interface to suggest content before the user searches.
const history = JSON.parse(localStorage.getItem('searchHistory')) || [];
if (history.length > 0) {
const suggestionBox = document.getElementById('search-suggestions');
suggestionBox.innerHTML = 'Try searching for: ' + history[0];
}
This adds a suggestion below the search box based on the most recent query.
Step 6: Localize Search Results Based on Language
If your knowledge base is multilingual, personalize results by detecting browser language:
const lang = navigator.language.slice(0, 2);
const filteredResults = results.filter(result => result.lang === lang);
This filters content so users primarily see results in their preferred language.
Step 7: Respect Privacy and Offer Control
Always provide users with the ability to clear their history or disable personalization. You can offer a simple reset button:
document.getElementById('reset-prefs').addEventListener('click', () => {
localStorage.clear();
alert('Your search preferences have been cleared.');
});
This ensures transparency and aligns with privacy best practices.
Step 8: Visual Feedback and UX Enhancements
Make the personalization feel alive by showing users that their behavior affects the system:
- Use badges or highlights to show “frequently visited.”
- Group search results into “based on your interest” vs. “all results.”
- Label suggestions as “recent” or “popular with you.”
Step 9: Cache Results Locally for Faster Access
To reduce repeated searches and speed up UX, cache the previous results:
sessionStorage.setItem('lastSearchResults', JSON.stringify(results));
Then reuse these results if the same term is used again in the same session.
Conclusion
While Jekyll and GitHub Pages are static platforms, adding personalized search functionality using client-side JavaScript is entirely feasible and surprisingly effective. By storing user data locally, ranking results dynamically, and showing relevant content, you can create a smart and adaptive user experience that rivals dynamic CMS platforms.
In the next article, we’ll cover how to build and visualize a feedback loop so users can rate articles — further training the system over time without a backend.
