Yearly archive for posts

Publii does not contain archives by date, but it is possible to create a helper which will create such functionality. To implement such feature we need two simple custom helpers. 

Helpers source code

We need to built two helpers:

  1. first will be used to generate years
  2. second will retrieve posts from a specific year

Create the helpers.js file and put the following code in it (if you have the helpers.js file in your theme - please copy only the yearsInArchive and getPostsByYear functions from the below code):

let themeHelpers = {
    yearsInArchive: function(startYear) {
        let years = [];
        let currentYear = new Date().getFullYear();
        for (let i = currentYear; i >= startYear; i--) {
          years.push(i);
        }
        return years;
    },
    getPostsByYear: function(posts, year) {
      return posts.filter(post => new Date(post.createdAt).getFullYear() === year);
    }
};
module.exports = themeHelpers;

The yearsInArchive helper returns every year from the given date. So for example for value 2016 it will return an array with the following values: 2021, 2020, 2019, 2018, 2017, 2016. You can reverse the order of years (if you want to use ascending order) by adding:

years.reverse();

Directly before line:

return years;

We will need this array to generate lists of posts grouped by years and using the creation date. If you need to group your posts by the last modification date - just replace post.createdAt with post.modifiedAt.

How to create yearly archive

You need to create post template and as its content please use the following code in place of the normal post content:

<h2>Blog archive</h2>

<ol>
     {{#each (yearsInArchive 2016) }}
     <li>
            <h3>{{ this }}</h3>
            
            {{#if (getPostsByYear @website.contentStructure.posts this)}}
                <ol>
                     {{#each (getPostsByYear @website.contentStructure.posts this)}}
                     <li>{{title}} - {{date createdAt 'YYYY-MM-DDTHH:mm'}}</li>
                     {{/each}}
                </ol>
            {{else}}
                <strong>No posts</strong>
            {{/if}}
     </li>
     {{/each}}
</ol>

The above code requires some explanation:

  • In the <ol> element we generate archive using the yearsInArchive helper. Please notice, that we use Handlebars subexpressions
  • To display the year, we are using the {{ this }} syntax - it will return years in the descending order.
  • To provide list of posts, we are using @website.contentStructure.posts global variable - it contains all posts of your website. Please remember - the contentStructure must be enabled in your theme.
  • Also we support a case when there are no posts in a specific year. Then the No posts text is displayed. 

Variant which displays only years with posts (it skips years without posts):

<h2>Blog archive</h2>

<ol>
     {{#each (yearsInArchive 2016) }}
     <li>
            {{#if (getPostsByYear @website.contentStructure.posts this)}}
            <h3>{{ this }}</h3>
            
            <ol>
                  {{#each (getPostsByYear @website.contentStructure.posts this)}}
                  <li>{{title}} - {{date createdAt 'YYYY-MM-DDTHH:mm'}}</li>
                  {{/each}}
            </ol>
            {{/if}}
     </li>
     {{/each}}
</ol>

In the above code we have removed No posts handling and we have moved year display inside condition which checks if some posts are available.

Please remember - you can use any Post item elements inside the #each loop which uses the getPostsByYear helper.

Summary

As you can see - creating yearly archive is not a complex task in Publii. It is also worth considering to create separate page for every year if you have a long list of posts. Then the additional loop with use of the yearsInArchive helper won't be required.