![](/assets/images/image-default.png)
Data File-Based Jekyll Breadcrumbs for Posts
Last time I added the breadcrumbs for pages only. Today I decided to also add breadcrumbs to the posts - the approach is similar, but different in a few aspects. I will write down what I did below.
By Default Every Posts has Breadcrumbs
I am not adding the possibility of opting out the breadcrumbs for a specific post with a front matter variable. The site as it is right now will need crumbs for every post, so I choose not to add more code to the front matter for now. Then the post layout contains the following code:
<!-- _layouts/post.html -->
<!-- ... -->
{% comment %} A post will always contain breadcrumbs {% endcomment %}
{%- include breadcrumbs-posts.html -%}
<!-- ... -->
The Include File
Similar to the pages breadcrumbs, the include file is very simple, since the logic lives mainly in the ruby code and data file.
<!-- _includes/breadcrumbs-posts.html -->
<div id="breadcrumbs" style="margin-bottom: 1rem;">
<a href="/">Home</a>
/
{% for crumb in breadcrumbs %}
<a href="{{ crumb.path | prepend:site.baseurl }}">
{{ crumb.text }}
</a>
/
{% endfor %}
<p href="#">{{page.title}}</p>
</div>
To notice: I find it a bit confusing that to extract the
title
from the front matter of a post using liquid it is necessary to usepage.title
(I keep making the mistake of writingpost.title
, which doesn’t work).
Logic Based On Post Categories
When adding the breadcrumbs for the pages, I added the breadcrumbs manually in the data file (because I have just a few pages). In the case of posts, I am only adding the breadcrumbs once per each category (which as of this date get assigned by using the post containing directory name). Some further relevant comments about this approach have been added together with the code below:
Jekyll::Hooks.register :posts, :pre_render do |post, payload|
# get the post category
# IMPORTANT - the code below works because:
# - All posts contain a `categories` front matter.
# - For the current site setup the `categories` variable contains only one category.
# - That category's breadcrumbs are provided in the corresponding data file.
# - TODO: consider improving code by managing multiple categories per post.
post_category = post.data["categories"][0]
if post.data["title"]
puts "Pre render of post '" + post.data["title"] + "' of category '" + post_category + "'"
else
puts "Pre render of a post without a title"
end
payload["custom_variable"] = "custom_value" # this will be accessible with liquid as custom_value
# get the breadcrumbs data file
require 'json'
breadcrumbs_data_path = File.join(Dir.pwd, '_data', 'breadcrumbs-posts.json')
breadcrumbs_data_text = File.read(breadcrumbs_data_path)
breadcrumbs_data = JSON.parse(breadcrumbs_data_text)
# find the breadcrumbs for the current post in the data file (based on post category)
breadcrumbs_found = false
breadcrumbs_data.each do |member|
if member["category"] == post_category
puts " Found breadcrumb data: #{member}"
payload["breadcrumbs"] = member["crumbs"] # this will be accessible with liquid as {"path"=>"/journals/", "text"=>"Journals"}{"path"=>"/site-updates/", "text"=>"Site Updates"}
breadcrumbs_found = true
break # Exit the loop after finding the member
end
end
if !breadcrumbs_found
payload["breadcrumbs"] = nil # if not found it should be empty
end
end
If I stop to think about it for a minute, I am writing the names of the categories twice: once in the directory names, and once in the data file. The advantages I see when keeping this setup are flexibility and simplicity of code - I could add more ruby code and make it more automated, but writing breadcrumbs only once per category manually is ok for me for now, and who knows maybe later the need for restructuring the site arises.
Data File
For the sake of completeness, I add the full data file of post breadcrumbs, _data/breadcrumbs-posts.json
, as of the date of today.
[
{
"category": "articles",
"crumbs": [
{
"path": "/articles/",
"text": "Articles"
}
]
},
{
"category": "journal-cs",
"crumbs": [
{
"path": "/journals/",
"text": "Journals"
},
{
"path": "/cs/",
"text": "C# .NET"
}
]
},
{
"category": "journal-other",
"crumbs": [
{
"path": "/journals/",
"text": "Journals"
},
{
"path": "/other/",
"text": "Other"
}
]
},
{
"category": "journal-revit-api",
"crumbs": [
{
"path": "/journals/",
"text": "Journals"
},
{
"path": "/revit-api/",
"text": "Revit API"
}
]
},
{
"category": "journal-site-updates",
"crumbs": [
{
"path": "/journals/",
"text": "Journals"
},
{
"path": "/site-updates/",
"text": "Site Updates"
}
]
},
{
"category": "tutorial-one-page",
"crumbs": [
{
"path": "/tutorials/",
"text": "Tutorials"
}
]
},
{
"category": "tutorial-vba",
"crumbs": [
{
"path": "/tutorials/",
"text": "Tutorials"
},
{
"path": "/vba/",
"text": "Learn VBA"
}
]
},
{
"category": "tutorial-git",
"crumbs": [
{
"path": "/tutorials/",
"text": "Tutorials"
},
{
"path": "/git/",
"text": "Learn Git"
}
]
}
]