Creating an “Archive” Page for Entries

You can create an “Archive” page, with entries grouped by month/year quite easily using Craft’s ‘group’ filter:

{% set allEntries = craft.entries.section('blog').limit(null) %}
{% for date, entries in allEntries | group("postDate|date('F Y')") %}
    <h2>{{ date }}</h2>
    <ul>
        {% for entry in entries %}
            <li>{{ entry.getLink() }}</li>
        {% endfor %}
    </ul>
{% endfor %}

The ‘group’ filter takes an array of items (entries, in this case) and groups them based on a common value. In this case we’re grouping them by a common Post Date format, “F Y”. (That’s PHP date format speak for “the full month name followed by the four-digit year”.)

Grouping by Year and then Month #

In the previous example we were only creating one level of headings – a combination of the month name and the year. If you would prefer to group entries by year first, and then by month, you can do that too using a nested ‘group’ filter:

{% set allEntries = craft.entries.section('blog').limit(null) %}
{% for year, entriesInYear in allEntries | group("postDate.year") %}
    <h2>{{ year }}</h2>
    <ul>
        {% for month, entriesInMonth in entriesInYear | group("postDate|date('F')") %}
            <h3>{{ month }}</h3>
            <ul>
                {% for entry in entriesInMonth %}
                    <li>{{ entry.getLink() }}</li>
                {% endfor %}
            </ul>
        {% endfor %}
    </ul>
{% endfor %}

Paginating the Archive #

If you have a ton of entries and feel it makes sense to span it across multiple pages, there are two ways you might want to go about that:

  • Split the entries into uniform chunks and use traditional pagination using the ‘paginate’ tag
  • Create yearly archive pages

We’ll go over both of those approaches.

Traditional Pagination #

Traditional pagination is quite simple to implement. Rather than grabbing all of our entries directly, we need to pass our params to the ‘paginate’ tag:

{% paginate craft.entries.section('blog').limit(100) as pageEntries %}
    {% for date, entries in pageEntries | group("postDate|date('F Y')") %}
        <h2>{{ date }}</h2>
        <ul>
            {% for entry in entries %}
                <li>{{ entry.getLink() }}</li>
            {% endfor %}
        </ul>
    {% endfor %}

    {% if paginate.prevUrl %}<a href="{{ paginate.prevUrl }}">Previous Page</a>{% endif %}
    {% if paginate.nextUrl %}<a href="{{ paginate.nextUrl }}">Next Page</a>{% endif %}
{% endpaginate %}

Yearly Archive Pages #

If you would prefer to dedicate each page to a full year of entries, you must first create a new Dynamic Route with the following settings:

  • Set the URI pattern to whatever you want the URI to look like leading up to the year (e..g “blog/archive/”), and then click on the ‘year’ token.
  • Set the Template setting to the path to your archive template.

Assuming that your template is publicly accessible (its name doesn’t start with an underscore), the first thing it should do is check to make sure that ‘year’ has been defined. In the event that it’s not, you have two choices on how to react:

  • Just default to the current year:

    {% if year is not defined %}
      {% set year = now.year %}
    {% endif %}
  • Redirect to the current year’s URL:

    {% if year is not defined %}
      {% redirect "blog/archive/"~now.year %}
    {% endif %}

Now let’s output the entries in the year, grouped by month:

<h1>{{ year }}</h1>

{% set entriesInYear = craft.entries.section('blog').limit(null).after(year).before(year+1) %}
{% for month, entries in entriesInYear | group("postDate|date('F')") %}
    <h2>{{ month }}</h2>
    <ul>
        {% for entry in entries %}
            <li>{{ entry.getLink() }}</li>
        {% endfor %}
    </ul>
{% endfor %}

To make this example complete, we’ll need to work in a custom navigation as well. Chances are you’re going to want to display all years between now and whenever the first entry was published. To do that, first we must fetch the first entry ever:

{% set firstEntryEver = craft.entries.section('blog').order('postDate asc').first() %}

Now that we have that, just loop through all of the possible years between then and now:

<ul>
    {% for year in now.year .. firstEntryEver.postDate.year %}
        <li><a href="blog/archive/{{ year }}">{{ year }}</a></li>
    {% endfor %}
</ul>