Hacking a Routing Component in Jekyll

Share this article

Key Takeaways

  • Jekyll lacks a built-in routing component, which can be problematic when URLs need to be changed and all links to the old URL must be updated manually. A workaround solution can be created using Liquid and Markdown, ideal for sites hosted on GitHub Pages where plugins and custom Ruby code cannot be used.
  • A router can be created in Jekyll by mapping URLs to names (routes) in a YAML file stored in the _data folder. This allows URLs to be changed without having to update all the links pointing to the old URL, as the links now refer to the route name rather than the URL path.
  • To make the router more user-friendly, Markdown link references can be autogenerated from the router, allowing routes to be used as references in any file. This is done by creating a Liquid partial that loops through all the routes and creates a Markdown reference for each one.
  • While this solution provides a functional router in Jekyll, there are some limitations. It only works in Markdown, not plain HTML, and anchors cannot be added to the link. Additionally, the HTML partial must be included in all pages, which can be tedious. Despite these drawbacks, this workaround provides a viable solution to the lack of a routing component in Jekyll.

Initially I was going to title this article Hacking a routing component in Jekyll when hosted on GitHub Pages with Markdown, Liquid, and YAML. Obviously, that’s a bit long so I had to shorten it. Anyway, the point doesn’t change: Jekyll lacks some kind of router, and I think I found a not-so-terrible solution to mimic one.

But first things first: I come from a Symfony background, so by router I mean a component that maps URLs to names — a.k.a. routes – so you can safely change a URL without having to crawl your codebase to update all the links heading to the obsolete location.

Why would we need such a thing?

I am currently working hard on the docs for SassDoc v2 (not released yet, as of this writing). We have quite a bit of content; over 20 pages split across 4 different sections containing many code examples and cross references.

A couple of times during the rewriting, I desired to change a URL. Problem is, when I change a URL I have to update all the links heading to this URL if I don’t want them to be broken. Crawling over 20 pages to make sure all URLs are alright is far from ideal…

This is why we need a routing component. Then we would refer to URLs by name rather than by their path, allowing us to change a path while keeping the name perfectly valid.

How does it work?

So what do we need to make this work? If you are running Jekyll but are not restricted to safe mode (which is unfortunately the case when using GitHub Pages for hosting), you can surely find/build a Ruby plugin to do this. This would be the best solution since this is typically something handled by a server-side language.

Now, if you host your site on GitHub Pages which is more often the case than not with Jekyll, you cannot use plugins nor can you extend Jekyll’s core with custom Ruby code, so you end up hacking a solution with what’s available: Liquid and Markdown.

The main idea is to have a file containing all our routes mapped to actual URLs. Thankfully, Jekyll allows us to define custom global variables through YAML/JSON/CSV files stored in the _data folder, later accessed via site.data.<filename>. Thus we can access those URLs directly in our pages from their name.

To add a little syntactic sugar on top, we’ll create Markdown link references that will allow a friendlier syntax – but let’s not go too fast too quickly.

Creating the router

The point of the router is to expose routes (a.k.a. names) mapped to URLs (one to one). It is possible to create YAML/JSON/CSV files in the _data folder of any Jekyll project, so let’s go with a YAML file named routes.yml:

home: "/"
about: "/about-us/"
faq: "/frequently-asked-questions/"
repository: "https://github.com/user/repository"

You may have noted that we are of course not restricted to internal links. We can totally define routes for external URLs to avoid typing them again and again if they tend to show up regularly. Along the same lines, we stopped at 4 routes for our example, but the router could contain hundreds of routes.

Because the file is in _data, we can access its content pretty much anywhere with site.data.<filename>.<key>. So let’s say we have a page containing the following code:

---
layout: default
title: "About us"
---

<!-- Content about us -->

Go to our [GitHub repository]({{ site.data.routes.repository }}).
Or read the section dedicated to [Frequently Asked Questions]({{ site.data.routes.faq }}).

As you can see, we no longer refer to URLs, but routes instead. This is not magic, it only tells Jekyll to access global variables stored at the given path (e.g. site.data.routes.faq).

Now, if the repository is no longer hosted on GitHub or the “About us” page’s URL is now /about/, not to worry! By updating the router, we make it work without having to come back to our pages to update our links.

Adding syntactic sugar

At this point, we have a functional router allowing us to change any URL without having to crawl our site to fix broken links. So you can say it is pretty cool already. However, having to type site.data.routes.faq is not very convenient. We could surely make it a bit more elegant!

Yes and no. At first, I thought of building a small route() function accepting a key name and returning the value stored at site.data.routes.<key>. The problem is we are running Jekyll in safe mode, so we cannot extend it with Ruby code. No luck.

Then I thought of a Markdown feature I never used before: link references. This is how a link is represented in Markdown:

[I am a link](http://link.url/)

You can also set the link to head to a reference — which is completely invisible by the way, defined anywhere in the page, like so:

[I am a link][id_reference]

​[id_reference]: http://link.url

Note: parentheses are replaced with brackets when using a reference rather than a URL.

This could allow you to have all your links defined in the same place (at the bottom for instance) rather than all over the document. I must say I haven’t used this feature much, but in this case it comes in handy.

The idea is to autogenerate link references from our router so we can use our routes as reference in any file. It turns out to be surprisingly easy to do this in Liquid:

# _includes/route.html
{% for route in site.data.routes %}
[{{ route[0] }}]: {{ route[1] }}
{% endfor %}

By adding this for loop anywhere in the page, this instructs Jekyll to process this as Liquid code, which will then be processed as Markdown references. So for instance, and coming back to our previous example, we could do:

---
layout: default
title: "About us"
---

<!-- Content about us -->

Go to our [GitHub repository][repository].
Or read the section dedicated to [Frequently Asked Questions][faq].

{% for route in site.data.routes %}
[{{ route[0] }}]: {{ route[1] }}
{% endfor %}

Now we’re talking, right? The only problem is having to include this loop in any page. At first, I thought of adding it in the layout, so it gets automatically added to any page using the relevant layout. The problem is that layouts are not processed as Markdown in Jekyll so the references actually come in visible at the bottom of the DOM. Even worse, they are not usable in our pages since they have not been processed as Markdown… Too bad.

However we can still do something to make it slightly better. We can put this loop in a Liquid partial and include the partial in every page rather than copy-pasting the loop. Say we create a routes.html partial in the _includes folder:

{% for route in site.data.routes %}
[{{ route[0] }}]: {{ route[1] }}
{% endfor %}

And then, in our page:

---
layout: default
title: "About us"
---
{% include routes.html %}

<!-- Content about us -->

Go to our [GitHub repository][repository].
Or read the section dedicated to [Frequently Asked Questions][faq].

Note: you can include the partial anywhere in the page, not only on top. You could totally put it as the very last line of the file.

Final thoughts

That’s it folks, we’ve hacked a little routing component in Jekyll safe-mode. Now what are the drawbacks of this? There are a few minor ones:

  • It works in Markdown; if you happen to have a link in plain HTML, you have to fall back to {{ site.data.routes.<key> }}, which is not too bad since it still keeps links safe from URL updates.
  • You cannot add an anchor to the link; if you need to, fall back to {{ site.data.routes.<key> }}#anchor (again, not too bad).
  • You have to include the HTML partial in all pages, which can be tedious (even if you consider this as an extension of the YAML front matter you have to write anyway).

Aside from those drawbacks, it’s all good and shiny. What do you think?

Frequently Asked Questions (FAQs) about Hacking the Routing Component in Jekyll

What is the significance of the routing component in Jekyll?

The routing component in Jekyll is crucial as it determines the structure of your site’s URLs. It is responsible for mapping each page to a specific URL, which is essential for site navigation and SEO. By hacking the routing component, you can customize your site’s URL structure to suit your specific needs, improving user experience and search engine visibility.

How can I customize the URL structure in Jekyll?

Jekyll allows you to customize your URL structure through the use of permalinks. You can specify a custom permalink in your post’s front matter, which will override the default URL structure. This allows you to create more descriptive and user-friendly URLs, which can improve your site’s SEO and user experience.

What are the benefits of using Jekyll for my website?

Jekyll is a simple, blog-aware, static site generator that’s perfect for personal, project, or organization sites. It’s incredibly flexible and supports custom URL structures, themes, plugins, and more. Plus, because it generates static sites, it’s incredibly fast and secure compared to traditional CMS platforms.

How can I deploy my Jekyll site to a third-party platform?

Jekyll supports a wide range of third-party deployment platforms, including GitHub Pages, Netlify, and more. To deploy your site, you’ll need to build it locally, then push the generated static files to your chosen platform. Each platform has its own specific deployment process, so be sure to check their documentation for detailed instructions.

What are some examples of websites built with Jekyll?

There are many impressive websites built with Jekyll, ranging from personal blogs to large organization sites. Some examples include the official Jekyll website, GitHub Pages, and many more. You can find a showcase of Jekyll websites on the official Jekyll website.

How can I use Cloudflare to deploy my Jekyll site?

Cloudflare Pages is a JAMstack platform for frontend developers to collaborate and deploy websites. It’s highly compatible with Jekyll and offers a simple, streamlined deployment process. You can connect your GitHub repository to Cloudflare Pages, then it will automatically build and deploy your site whenever you push to your repository.

What are some resources for learning more about Jekyll?

The official Jekyll website is a great resource for learning more about Jekyll. It offers comprehensive documentation, tutorials, and a showcase of Jekyll websites. Additionally, there are many community resources available, including forums, blogs, and GitHub repositories.

How can I add plugins to my Jekyll site?

Jekyll supports a wide range of plugins that can add new features and functionality to your site. To add a plugin, you’ll need to install it, then add it to your site’s _config.yml file. Some plugins may also require additional configuration, so be sure to check the plugin’s documentation for detailed instructions.

Can I use Jekyll with other programming languages?

Jekyll is built with Ruby, but it can be used with other programming languages through the use of plugins and integrations. For example, you can use Jekyll with JavaScript to add dynamic features to your site, or with CSS preprocessors like Sass to streamline your styling process.

How can I optimize my Jekyll site for SEO?

Jekyll offers several features that can help optimize your site for SEO. This includes custom URL structures, support for meta tags, and the ability to generate an XML sitemap. Additionally, because Jekyll generates static sites, it’s incredibly fast, which can improve your site’s search engine rankings.

Kitty GiraudelKitty Giraudel
View Author

Non-binary trans accessibility & diversity advocate, frontend developer, author. Real life cat. She/her.

jekyllLouisLrouting componenturl mapping
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week