build a user feedback loop in jekyll without backend
Why Feedback Matters in Static Knowledge Bases
Most websites evolve based on user behavior and direct feedback. But when you’re running a static site using Jekyll on GitHub Pages, collecting feedback becomes more complex — no server-side logic, no database. The good news? With some creative use of JavaScript and browser storage, you can still capture useful signals and build a feedback loop.
Use Cases for Static Feedback
In the context of a knowledge base, feedback helps answer:
- Was this article helpful?
- Did users find the content they were searching for?
- Which topics should be expanded or improved?
Step 1: Build a Simple Voting Interface
Let’s add a feedback box to every article using a simple “Yes/No” prompt:
<div class="feedback-box">
<p>Was this article helpful?</p>
<button onclick="submitFeedback(true)">Yes</button>
<button onclick="submitFeedback(false)">No</button>
</div>
Step 2: Store Feedback Locally
Since you can’t send feedback to a backend, store it locally per post using the page URL as the key.
function submitFeedback(value) {
const key = window.location.pathname;
const feedbackData = JSON.parse(localStorage.getItem('feedbackData')) || {};
feedbackData[key] = value;
localStorage.setItem('feedbackData', JSON.stringify(feedbackData));
document.querySelector('.feedback-box').innerHTML = 'Thanks for your feedback!';
}
Step 3: Track and Visualize Aggregate Feedback
Over time, you may want to show users what they’ve voted on, or create a dashboard view for yourself. This is possible using session aggregation.
function getFeedbackSummary() {
const data = JSON.parse(localStorage.getItem('feedbackData')) || {};
const total = Object.keys(data).length;
const positive = Object.values(data).filter(x => x).length;
const negative = total - positive;
return { total, positive, negative };
}
Then you could render a simple display somewhere in the footer or sidebar:
const summary = getFeedbackSummary();
document.getElementById('summary-box').innerText =
`You’ve rated ${summary.total} pages. Helpful: ${summary.positive}, Needs improvement: ${summary.negative}`;
Step 4: Build a Personalized “Improvement List”
Help users help themselves. If they marked an article unhelpful, suggest better ones next time.
const feedbackData = JSON.parse(localStorage.getItem('feedbackData')) || {};
const unhelpfulPages = Object.keys(feedbackData).filter(key => !feedbackData[key]);
const suggestions = allPosts.filter(post => !unhelpfulPages.includes(post.url));
renderSuggestions(suggestions.slice(0, 3));
This avoids showing previously disliked content, improving personalization.
Step 5: Use Feedback to Trigger Conditional Prompts
Feedback doesn’t need to be passive. Trigger follow-up questions based on answers:
function submitFeedback(value) {
const key = window.location.pathname;
const feedbackData = JSON.parse(localStorage.getItem('feedbackData')) || {};
feedbackData[key] = value;
localStorage.setItem('feedbackData', JSON.stringify(feedbackData));
if (!value) {
const box = document.querySelector('.feedback-box');
box.innerHTML = '<p>What could be improved?</p><input id="feedback-detail"><button onclick="saveDetail()">Send</button>';
} else {
document.querySelector('.feedback-box').innerHTML = 'Thanks!';
}
}
function saveDetail() {
const detail = document.getElementById('feedback-detail').value;
sessionStorage.setItem('lastFeedbackDetail', detail);
document.querySelector('.feedback-box').innerHTML = 'Thanks for your input!';
}
Step 6: Respect Privacy and Offer Transparency
Since everything is stored locally, privacy is preserved. Still, users should know how it works and be given a reset option:
<button onclick="localStorage.clear(); alert('Your feedback history has been cleared.')">Clear Feedback</button>
Step 7: Bonus — Export Feedback for Review
If you're the site owner, you might want to inspect user feedback by asking them to export and send it:
<button onclick="downloadFeedback()">Export Feedback</button>
function downloadFeedback() {
const data = localStorage.getItem('feedbackData');
const blob = new Blob([data], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'feedback.json';
a.click();
URL.revokeObjectURL(url);
}
This gives power users the ability to share insights manually without compromising the site's static nature.
Conclusion
Even without a backend, it’s entirely possible to implement a basic yet useful feedback loop in Jekyll using only client-side tools. By gathering, storing, and responding to local feedback, you enhance the user experience and create the impression of a responsive, evolving knowledge base — all while staying within the boundaries of GitHub Pages hosting.
In the next article, we’ll dive into making your search and feedback mechanisms accessible and inclusive for all users.
