2022-07-10 — 3 min read
Tagged: jekyll

Jekyll Layouts Explained

To eliminate repeating the same structure (HTML boilerplate, navbar, footer, etc.) in every post, Jekyll provides layouts. Let’s see how to use them!

Default layout

The default layout of a freshly generated Jekyll site is the following:

<!DOCTYPE html>
<html lang="{{ site.lang | default: "en-US" }}">
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta charset="utf-8">
    <title>{{ page.title }} - {{ site.title }}</title>
    <link rel="stylesheet" href="{{ "/assets/css/main.css" | relative_url }}">
  </head>
  <body>
    {{ content }}
  </body>
</html>

It’s a basic HTML boilerplate, with a couple of additions:

<html lang="{{ site.lang | default: "en-US" }}">

This line sets the language of the page to what’s set in _config.yml (accessible as site.lang). The rest of the expression is a liquid filter: if the site.lang variable is not set, it’ll return en-US as a default language.

    <title>{{ page.title }} - {{ site.title }}</title>

This line sets the title of the page to a combination of two title variables: the one set in the page’s front matter (page.title) and the one set in _config.yml (site.title), separated by space-dash-space. (In addition to page and site, we could also set a variable in the layout’s front matter, then we’d access it as layout.variable_name.)

There is a slight problem with the default title: what happens if the page’s title is not set? (This shouldn’t happen often: Jekyll will set the title to the post’s filename by default, which can be overridden by the front matter. It could happen, for example, with pages generated by the jekyll-archive gem for the various dates.) If page.title is unset, the HTML title attribute will be incomplete: - site_title instead of page_title - site_title. We can fix it with a default filter if we set a default title in the layout’s front matter:

    <title>{{ page.title | default: layout.title }} - {{ site.title }}</title>

Lastly, the content variable holds the complete rendered text of the post, which is inserted as-is in the body tag in the default layout.

  <body>
    {{ content }}
  </body>

Customizing the layout

Most sites contain various fixed elements on every page: included stylesheets, javascript code, a navigation bar, or a footer, for example. We can include these elements in the layout once, and they’ll be there on every page, we only need to add the part that varies: the content.

We can add the above elements to the layout in two ways: we can either add them directly to the layout itself, or create them in a separate file, and include that file in the layout. The latter allows greater modularity: we can create the navbar in a separate file, _includes/navbar.html (includes are stored in that folder), and add it to the layout with this line:

{% include navbar.html %}

Since we can access the variables set in the page’s front matter from the layout, we can turn on/off various parts of the layout, depending on the current page. One example is the 404 page: it’ll need a couple of extra lines in the header that the other pages won’t. The easiest way to do it is to add the line

redirect: /

to the 404.html page’s front matter, and in the layout, check for this variable:

{% if page.redirect %}
<link rel="canonical" href="{{ page.redirect }}">
<script>location="{{ page.redirect }}"</script>
<meta http-equiv="refresh" content="0; url={{ page.redirect }}">
<meta name="robots" content="noindex">
{% endif %}

We won’t include this variable in the front matter of any other pages, so this part will only be generated for the 404 page.

The reason to do it this way is the following: Jekyll has layout inheritance, but it can only nest one layout in another, we can’t change parts of the parent layout from the child. So, if we have a default layout like the one at the top of the post, we won’t be able to add extra lines to the header of the HTML from the child layout (as it is required for the 404 redirects), we can only affect the content part of the parent layout, which is inside the <body> tag.

The only way other than using a page variable would be to create a whole new layout for the redirects, which would be a copy of the default layout with the needed bits added. But this is against the DRY principle: now with every update or change of the layout, we’d have to modify the code in two places: the default layout and the redirect layout. So it’s more practical to use the above method, and add the needed part depending on whether the variable is set in the page’s front matter or not.

Thanks for reading! If you have any comments, additions, or corrections, feel free to reach me via e-mail.

Copyright © 2023 csm.hu
Contact