Data File-Based Jekyll Breadcrumbs for Pages
After doing some research online, I decided to implement breadcrumbs by combining a hook and a data file.
My reasoning behind this decision is:
- I want to have more logic living in the plugins and not in liquid code.
- I want for my permalinks to not contain data related to the categories, because of what I mentioned in this old post.
Today I implemented the breadcrumbs for pages only. I might add them to the posts too in the future. Here is how I did it:
- Each page has a front matter variable that indicates if the breadcrumbs are shown or not:
# bungee-jumping-tutorial.html
---
#...
breadcrumbs_visible : true
#...
---
- Then in the page layout, an include is loaded with the breadcrumbs code if the front matter variable is true:
- The include file looks like this:
Important to notice: The home link at the beggining, and the current page text at the end are always present, independently from the data file content (the
breadcrumbs
variable).
- Finally, as mentioned before, the heavy lifting is done by the hook and the data file:
- The data file contains for each page a list of the link and text of each breadcrumb (excluding the first and last that are pre-established).
- The hook finds the correct data in the data file based on the file name of the page (excluding its extension), and then makes the breadcrumbs accessible through Jekyll.
Jekyll::Hooks.register :pages, :pre_render do |page, payload|
# get the file name of the page without extension
file_name = File.basename(page.path)
file_name_without_extension = File.basename(file_name, File.extname(file_name))
if page.data["title"]
puts "Pre render of page (file name "+ file_name_without_extension +"): " + page.data["title"]
else
puts "pre render of a page without a title"
end
# get the breadcrumbs data file
require 'json'
breadcrumbs_data_path = File.join(Dir.pwd, '_data', 'breadcrumbs.json')
breadcrumbs_data_text = File.read(breadcrumbs_data_path)
breadcrumbs_data = JSON.parse(breadcrumbs_data_text)
# find the breadcrumbs for the current page in the data file
breadcrumbs_found = false
breadcrumbs_data.each do |member|
if member["page"] == file_name_without_extension
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
- This is the exact data file (
_data/breadcrumbs.json
) I am using as of the date of writing this post. Eachcrumbs
list right now has only one breadcrumb, but there could be more if necessary.
[
{
"page": "git",
"crumbs": [
{
"path": "/tutorials/",
"text": "Tutorials"
}
]
},
{
"page": "journal-other",
"crumbs": [
{
"path": "/journals/",
"text": "Journals"
}
]
},
{
"page": "journal-site-updates",
"crumbs": [
{
"path": "/journals/",
"text": "Journals"
}
]
},
{
"page": "journal-revit-api",
"crumbs": [
{
"path": "/journals/",
"text": "Journals"
}
]
},
{
"page": "journal-cs",
"crumbs": [
{
"path": "/journals/",
"text": "Journals"
}
]
}
]
Important to notice: as of today the site has very few pages and many more posts - that’s why this approach works fine in my case. If someday I want to implement post breadcrumbs as well, I’ll probably make sure it requires fewer manual inputs.
And that’s how it is working as of today. I know it is not perfect, but I am happy with this as a first iteration. I’ll probably be improving this system in the future.