context-aware related posts in jekyll using liquid

Why Contextual Related Posts Improve Engagement Most "Related Posts" systems simply match by tags or categories. While this works in many cases, it can feel too broad. A more intelligent approach is to show posts that are related based on the user's context or the post’s core theme. This increases reader retention and keeps your content experience focused. We’ll use Jekyll’s powerful Liquid syntax to achieve context-sensitive recommendations. Core Idea Behind Context-Aware Related Posts Instead of relying solely on tags or categories, you can: Define a primary topic for each post Group related posts by series , pillar , or type Set up fallback logic if no strong match exists This way, you can recommend: Other parts of a tutorial series Posts targeting the same user intent Posts with the same use case (e.g. Jekyll for documentation) Step-by-Step: Smarter Related Posts Step 1: Define Contextual Fields In your post front matter, add...

boosting related posts with flexsearch for jekyll blogs

Why FlexSearch for Related Posts

While Lunr.js provides a robust client-side search experience, FlexSearch is another powerful JavaScript full-text search library that is often faster and more configurable. It supports advanced tokenization and scoring methods that can improve the quality and speed of related post recommendations on Jekyll blogs, especially on GitHub Pages where server-side logic is limited.

Advantages of FlexSearch

  • Higher performance and lower memory footprint compared to Lunr
  • Support for async indexing and searching
  • Multiple search modes (e.g., "match", "score", "strict") for fine-tuned control
  • Better support for complex language processing and custom tokenization

Step 1: Generating JSON Data for FlexSearch

The JSON structure is similar to Lunr’s, but you can include additional fields or customize as needed:


---
layout: null
permalink: /flexsearch-posts.json
---

[
  {% for post in site.posts %}
  {
    "id": "{{ post.url }}",
    "title": {{ post.title | jsonify }},
    "tags": {{ post.tags | join: ' ' | jsonify }},
    "content": {{ post.content | strip_html | jsonify }}
  }{% unless forloop.last %},{% endunless %}
  {% endfor %}
]

Step 2: Adding FlexSearch Script

Add FlexSearch to your post layout via CDN:


<script src="https://cdn.jsdelivr.net/npm/flexsearch@0.7.31/dist/flexsearch.bundle.js"></script>

Step 3: Creating the FlexSearch Related Posts Function

We build a client-side script that:

  • Fetches the JSON data
  • Indexes the posts asynchronously
  • Searches based on the current post title
  • Displays top relevant related posts excluding the current post

<script>
document.addEventListener("DOMContentLoaded", function () {
  const currentURL = window.location.pathname;
  const relatedContainer = document.getElementById("related-posts");

  fetch("/flexsearch-posts.json")
    .then(res => res.json())
    .then(posts => {
      const index = new FlexSearch.Document({
        document: {
          id: "id",
          index: ["title", "tags", "content"],
          store: ["title", "id"]
        },
        tokenize: "forward",
        cache: true,
        async: true,
        resolution: 9,
      });

      posts.forEach(post => index.add(post));

      const currentPost = posts.find(p => p.id === currentURL);

      if (!currentPost) return;

      index.searchAsync(currentPost.title, {limit: 5}).then(results => {
        const relatedPosts = [];

        results.forEach(result => {
          result.result.forEach(id => {
            if (id !== currentURL && !relatedPosts.find(p => p.id === id)) {
              const post = posts.find(p => p.id === id);
              if (post) relatedPosts.push(post);
            }
          });
        });

        if (relatedPosts.length > 0 && relatedContainer) {
          relatedContainer.innerHTML = `
            <h3>Related Posts Powered by FlexSearch</h3>
            <ul>
              ${relatedPosts.map(p => `<li><a href="${p.id}">${p.title}</a></li>`).join("")}
            </ul>
          `;
        }
      });
    });
});
</script>

Step 4: Adding the Container

Place this in your post layout template where you want the related posts list:

<div id="related-posts"></div>

Real-World Example and Performance

On a mid-sized Jekyll blog with 200+ posts, FlexSearch indexing completed under 200ms in the browser, with searches returning results instantly. Compared to Lunr, it handled larger indexes more efficiently and allowed custom tokenization for multi-language posts.

Improving Relevance with Weighted Fields

You can prioritize some fields, e.g., tags over content, using weights in FlexSearch:


const index = new FlexSearch.Document({
  document: {
    id: "id",
    index: [
      { field: "title", tokenize: "forward", weight: 5 },
      { field: "tags", tokenize: "forward", weight: 10 },
      { field: "content", tokenize: "forward", weight: 1 }
    ],
    store: ["title", "id"]
  },
  async: true,
});

Conclusion

FlexSearch offers a performant and flexible way to implement full-text related post search on static Jekyll sites hosted on GitHub Pages. Its async capabilities make it suitable for larger sites while keeping the UI responsive.

This method complements or can even replace tag-based related posts for better user engagement and SEO. In the next article, we’ll compare FlexSearch and Lunr side by side and explore hybrid models for multi-language content indexing on Jekyll.


Archives / All Content


© GlowAdHive🕒😀😃😀 . All rights reserved.