If youâve already implemented related posts by tags in your GitHub Pages blog, youâve taken a great first step toward improving content discovery. But tags alone sometimes miss context â for example, two posts might share the same tag but belong to entirely different topic branches. To fix that, you can combine tags and categories into a single scoring system to create smarter, more accurate related post suggestions.
Why Combine Tags and Categories
In Jekyll, both tags and categories are used to describe content, but in slightly different ways:
- Categories describe the main topic or section of the post (like SEO or Development).
- Tags describe the details or subtopics (like on-page, liquid, optimization).
By combining both, your related posts logic becomes far more contextual. It can prioritize posts that share both a category and tags over those that only share tags, giving you layered relevance.
Building the Smart Matching Logic
Letâs start by creating a Liquid loop that gives each post a âmatch scoreâ based on overlapping categories and tags. A post sharing both gets a higher score.
Step 1 Define Your Scoring Formula
In this approach, weâll assign:
- +2 points for each matching category.
- +1 point for each matching tag.
This way, Jekyll can rank related posts by how similar they are to the current one.
{% assign related_posts = site.posts | where_exp: "item", "item.url != page.url" %}
{% assign scored = "" %}
{% for post in related_posts %}
{% assign cat_match = post.categories | intersection: page.categories | size %}
{% assign tag_match = post.tags | intersection: page.tags | size %}
{% assign score = cat_match | times: 2 | plus: tag_match %}
{% if score > 0 %}
{% capture item %}
{{ post.url }}::{{ post.title }}::{{ score }}::{{ post.image }}
{% endcapture %}
{% assign scored = scored | append: item | append: "|" %}
{% endif %}
{% endfor %}
This snippet calculates a weighted relevance score for every post that shares at least one tag or category.
Step 2 Sort and Display by Score
Liquid doesnât directly sort by custom numeric values, but you can achieve it by converting the string into an array and reordering it manually. To keep things simple, weâll display only the top few posts based on score.
Recommended for You
Each related post now comes with its thumbnail, title, and an implicit relevance score based on shared categories and tags.
Styling the Related Section
You can reuse the same CSS grid used in the previous ârelated posts with thumbnailsâ article, or make this version slightly more compact for emphasis on content relationship:
.related-hybrid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
list-style: none;
margin: 2rem 0;
padding: 0;
}
.related-hybrid li {
background: #f7f7f7;
border-radius: 10px;
overflow: hidden;
transition: transform 0.2s ease;
}
.related-hybrid li:hover {
transform: translateY(-3px);
}
.related-hybrid img {
width: 100%;
height: 120px;
object-fit: cover;
}
.related-hybrid span {
display: block;
padding: 0.75rem;
text-align: center;
color: #333;
font-size: 0.95rem;
}
Adding Weight Control for SEO Context
You can tweak the scoring weights if your blog emphasizes certain relationships. For example:
- If your site has broad categories, give tags higher weight since they reflect finer topical depth.
- If categories define strong topic boundaries (e.g., âPhotographyâ vs. âProgrammingâ), give categories higher weight.
Simply adjust the Liquid logic:
{% assign score = cat_match | times: 3 | plus: tag_match %}
This makes categories three times more influential than tags when calculating relevance.
Practical Example
Letâs say you have three posts:
| Title | Categories | Tags |
|---|---|---|
| Mastering Jekyll SEO | jekyll,seo | optimization,metadata |
| Improving Metadata for SEO | seo | metadata,on-page |
| Building Fast Jekyll Themes | jekyll | performance,speed |
When viewing âMastering Jekyll SEO,â the second post shares the seo category and metadata tag, scoring higher than the third post, which only shares the jekyll category. As a result, it appears first in the related section â reflecting better topical relevance.
Handling Posts Without Tags or Categories
If a post doesnât have any tags or categories, the related section might render empty. To handle that gracefully, add a fallback message:
{% if scored == "" %}
No related articles found. Explore our latest posts instead:
{% for post in site.posts limit: 3 %}
- {{ post.title }}
{% endfor %}
{% endif %}
This ensures your layout stays consistent and always offers navigation options to readers.
Combining Smart Matching with Thumbnails
You can enhance this further by mixing the smart scoring logic with the thumbnail display method from the previous tutorial. Add the image variable for each post and include fallback support.
{% assign default_image = "/assets/images/fallback.webp" %}
This ensures every related post displays a consistent thumbnail, even if the post doesnât define one.
Performance and Build Efficiency
Since this method uses simple Liquid loops, it doesnât affect GitHub Pages build times significantly. However, you should:
- Use
limit: 5in your loops to prevent long lists. - Optimize images for web (WebP preferred).
- Minify CSS and enable lazy loading for thumbnails.
The final result is a visually engaging, SEO-friendly, and contextually accurate related post system that updates automatically with every new article.
Final Thoughts
By combining tags and categories, youâve built a smart hybrid related post system that mimics the intelligence of dynamic CMS platforms â entirely within the static simplicity of Jekyll and GitHub Pages. It enhances user experience, internal linking, and SEO authority â all while keeping your blog lightweight and fully static.
Next Step
In the next continuation, weâll explore how to add JSON-based structured data to your related post section so that Google better understands post relationships and can display enhanced results in SERPs.