53

I am using Jekyll as a static generator for a website (not a blog), and I want to have an automatically generated list of all pages on my index page. Specifically, I want to have different categories and list all articles in each category separately. Here's an example of what I'm describing, if you're having trouble following. Is there any way to do this in Jekyll (e.g. GitHub pages)? I've seen the variables documentation page but that seems specific to the blog post format.

NH.
  • 1,858
  • 2
  • 22
  • 35
Mike
  • 1,509
  • 2
  • 14
  • 20
  • 'Here's an example of what I'm describing, if you're having trouble following.' FWIW, that page isn't automatically generated - I add entries manually, when I think they're finished and of high quality. – gwern Jun 22 '13 at 21:23
  • I had just decided to do the same thing, gwern. – Mike Jun 23 '13 at 16:22

4 Answers4

87

While building my own site I came across this very same problem, and I have found an (IMHO) easy and robust solution. Hopefully this is useful for anyone else wishing to do something similar.

The Problem

Given a subset of pages (not posts) on the site, list them under headings based on their categories. For example: given a set of pages which we consider resource pages (or reference pages, or whatever logical grouping of pages that you want to display are), we want to list them under their categories (ex. code, explanation, et cetera).

The Solution

To get the behaviour that we want, we have to make modifications in three places:

  • _config.yml
  • resources.md
  • resource-file-X.md

_config.yml

In _config.yml, we must add a list of all of the categories/keywords/tags (or whatever you want to call it) that will appear in the resource files. Here is what I have in mine:

category-list: [code, editors, math, unix]

You can call the variable anything, I chose category-list, just make sure that you use the same variable in the resource.md file.

Note: The order that you place the items in the list is the order they will be listed on the resource.md page.

resource-file-X.md

These are the files that you want to have indexed and linked to on the resources.md page. All that you need to do is add two file variables to the top of each of these files. The first is to indicate that this file is a resource file.

resource: true

The second is to indicate what categories you want this file to be indexed under. You can index it under as many categories as you would like, and if you want a page un-indexed, leave the list blank. My reference for proper EINTR handling in C has the following categories:

categories: [code, unix]

resources.md

This is the file that will generate the list of pages based on their respective categories. All you need to do is add the following code to this file (or whatever file you want the list to be on):

{% for cat in site.category-list %}
### {{ cat }}
<ul>
  {% for page in site.pages %}
    {% if page.resource == true %}
      {% for pc in page.categories %}
        {% if pc == cat %}
          <li><a href="{{ page.url }}">{{ page.title }}</a></li>
        {% endif %}   <!-- cat-match-p -->
      {% endfor %}  <!-- page-category -->
    {% endif %}   <!-- resource-p -->
  {% endfor %}  <!-- page -->
</ul>
{% endfor %}  <!-- cat -->

Code Breakdown

Just a quick explanation of how this works:

  • Loop through each of the categories specified in _config.yml.
  • Display a heading with that category name.
  • Start an unordered list for the pages that belong in that category.
  • Loop through the pages on the site.
  • If the page is a resource file as indicated by the file variable resource, then for each of the categories that the file belongs to, if one of them matches the current category being listed, display a link to that page.

Note: the variables category-list in _config.yml and categories in the resource files can be called whatever you want, just make sure that you use the same variables in the file generating the list.

Another Note: When you modify _config.yml, you have to completely restart Jekyll, even if you have the --watch option, you have to stop and restart it. It took me a while to figure out why my changes weren't taking effect!

The Final Product

You can see the final product on the resources page on my site, although I just put this together today so at the time of this writing, it's far from complete, but you can check out my bio if you want on the home page.

Hope this helps!

m-renaud
  • 1,225
  • 8
  • 11
  • What exactly is this `ressource` file ? where should I put it ? thanks – giac Jul 17 '15 at 13:46
  • Where do the resource and resource-name-x.md files go? – Jwan622 Aug 03 '15 at 10:36
  • 2
    Sorry for the late reply, I must have missed the notification. @giacomoV, the term "resource" is completely arbitrary, that's just the use case that I had, you could replace this with "recipes", "codelabs", "tutorials", etc. Pretty much anything that's not a "post" as it is defined in Jekyll. Hopefully that makes sense :P – m-renaud Sep 28 '15 at 02:29
  • @Jwan622 I typically put the resource-name-x.md files in sub-directories of /resource, for example /resource/codelabs/{foo,bar,baz}-codelab.md. The file resource.md usually just ends up being /resource/index.md. – m-renaud Sep 28 '15 at 02:32
18

There's a cleaner way to do this using the liquid "contains" property:

In _config.yml, add your index of categories

categories: [fruit, meat, vegetable, cheese, drink]

In your page.md inside the front matter, add one or more of the categories available in the _config.yml

---
layout: page
title: Orange juice
description: Orange juice is juice from oranges. It's made by squeezing oranges.
categories: [fruit, drink]
---

In your template to get all the pages in the fruit category you do:

{% for page in site.pages %}
  {% if page.categories contains 'fruit' %}
    <div class="item">
      <h3>{{page.title}}</h3>
      <p>{{page.description}}</p>
    </div>
  {% endif %}
{% endfor %}
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
felipesk
  • 439
  • 4
  • 5
  • This doesn't actually implement what OP is asking for. It also requires you to manually define all of the categories in _config, which is burdensome. – Robert Liberatore Feb 14 '17 at 17:34
2

You should differentiate between pages and posts (articles). Listing all posts sorted by category is not a problem at all. You can loop through site.categories. It contains the category name and a list of all posts in that category.

Listing all pages is possible, too. You can loop through site.pages. But a page does not belong to a specific category (only posts do).

When I take a look at your posted example, using categories on posts and then looping through site.categories seems to be the way to go. It will get you exactly the desired output.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Polygnome
  • 7,159
  • 2
  • 30
  • 55
  • 1
    This is indeed the limit of the functionality that Jekyll provides, but it doesn't work for my needs. I think I'm going to do this by writing a bash script that builds the site and then calls a Haskell script that inserts the page links by category. – Mike Jun 17 '13 at 15:02
  • 1
    Using posts locks me into a bunch of stuff that I don't want (needing to use a specific naming format, keeping them all in the same directory, forcing me to use the "post" layout) but there is no way to use categories on pages within Jekyll. – Mike Jun 17 '13 at 15:56
  • You do neither need to keep them all in the same directory nor are you forced to use the 'post' layout. posts will use the 'post' layout by default, but you can specify the layout for each post individually via YAML Front Matter (in fact, that was mandatory in jekyll < 1.0 ). It's true, you need to use a specific naming convention, but is that really the game-breaker? – Polygnome Jun 17 '13 at 15:59
  • You can even get rid of the naming issue by using drafts and setting `show_drafts: true` in your _config.yml ;) – Polygnome Jun 17 '13 at 16:06
1

There are some variations/ simplifications possible (answer of felipesk). Maybe due to improvements in Jekyll.

There is NO index needed in _config.yml.

If the list of pages are not listed in a page but for example in a doc, you can add the category also to the doc:

---
layout: doc
title: Fruit List
categories: [fruit]
---

And then use it like this:

{% for p in site.pages %}
   {% if p.categories contains page.category %}
     * [{{ p.title }}]({{ p.url | absolute_url }})
        <small>{{ p.excerpt }}</small>
   {% endif %}
{% endfor %}

With posts this can even be shorter:

{% for post in site.categories[page.category] %}
  * [{{ post.title }}]({{ post.url | absolute_url }})  
      <small>{{ post.excerpt }}</small>
{% endfor %}

Why this only works for posts, I could not figure out yet.

The interesting point is that this snippet can be used everywhere (if you mix docs/pages/posts)! So just add it as an _includes and use it like:

## Further Reading
{% include pages-list.md %}

I work with the theme Minimal Mistakes

pme
  • 11,442
  • 2
  • 35
  • 62