Karly Nelson

The Problems

  1. You want to show the last modified date of a post (or item in any collection) without having to remember to edit it manually every time.
  2. You want to format a date nicely.

Solution Summary

Use "Last Modified" or "git Last Modified" as your date in your frontmatter.

date: Last Modified

Use toLocaleString to format your date

<!-- In a page  -->
{{ page.date.toLocaleString()}}
// Or custom filter
eleventyConfig.addFilter("postDate", (dateObj) => {
  return dateObj.toLocaleString(undefined, {
    year: "numeric",
    month: "long",
    day: "numeric",
  });
});

The Explanation

Use last modified date

The simplest way to show the last modified date of a post (or item in any collection) without having to remember to edit it manually every time is by setting "Last Modified" as your date in the frontmatter of the post.

Note: See 11ty dates docs for other date options like git Last Modified. Whereas Last Modified "automatically resolves to the file’s last modified date", git Last Modified "automatically resolves to the file’s latest git commit." This is a bit more resource intensive though, so you might want to use a posts.11tydata.js solution. I think the main difference is Last Modified might count a file as modified if its layout was modified. Not 100% sure about that though. Generally though, the git one seems a bit more accurate.

Update frontmatter

In your 11ty collection item, update the date in your frontmatter. For example, the frontmatter of this post (posts/how-to-handle-dates-in-11ty.md) would be:

---
layout: layouts/post.njk
tags: post
title: How to handle dates in 11ty
date: Last Modified
draft: true
---
## A heading
The content of your post goes here...

I use YAML syntax. You can use other formats too.

Use in pages

You can then use the date in pages or layouts.

For example, in my posts/index.njk page I have a list of posts:

{%- for post in collections.post -%}
<li>
  <a href="{{ post.url }}">
    <h2>{{ post.data.title }}</h2>
    <p>Last updated: {{ post.date }}</p>
  </a>
</li>
{%- endfor -%}

Note that for looping through a collection list we use post.data.

and I show the date in my _includes/post.njk Post page layout:

<article>
  <h1>{{title}}</h1>
  <p>Last updated: {{page.date}}</p>
  <div>{{ content | safe }}</div>
</article>

Note that for a template layout we use page.date instead of just date. See 11ty docs

Format date

This shows up as Thu Sep 19 2024 08:38:16 GMT-0500 (Central Daylight Time). You might want a shorter date or to format it differently.

In our _includes/post.njk Post page layout, where we're using page.date, we can call Javascript methods on the output like {{page.date.toLocaleString()}}. This only works in Nunjucks though, not LiquidSee Docs. "You could add your own toUTCString filter in Liquid to perform the same task."

In _includes/post.njk:

<article>
  <h1>{{title}}</h1>
  <p>Last updated: {{page.date.toLocaleString()}}</p>
  <div>{{ content | safe }}</div>
</article>

If we use toLocaleString() our date Thu Sep 19 2024 08:38:16 GMT-0500 (Central Daylight Time) will become 9/19/2024, 9:40:51 AM.

You can pass options to toLocaleString() to change how you want it formatted - like if you want it to show the time or not.

Example toLocaleString formatting

toLocaleString takes two arguments, locales and options, like toLocaleString(locales, options);

locales is a string. For the locales we can either define one like "en-US" or "ko-KR" and it will render the dates how they should look in those places (the US and Korea, respectively).

If we just want it to render based on where the user is located, we pass in undefined so it will grab the user's default language for their locale.

options is an object. If we want our date formatted like September 19, 2024, we'd pass in the options like this:

const options = {
  year: "numeric",
  month: "long",
  day: "numeric",
};

const locales = undefined;

const date = new Date(); // gets today's date by default

date.toLocaleString(locales, options);

Read the docs to see all your options.

How we'd use this in _includes/post.njk:

<p>
  Last updated: {{page.date.toLocaleString( undefined, { year: 'numeric', month:
  'long', day: 'numeric' })}}
</p>

How we'd use this in posts/index.njk:

{%- for post in collections.post -%}
<li>
  <a href="{{ post.url }}">
    <h2>{{ post.data.title }}</h2>
    <p>
      Last updated: {{post.date.toLocaleString( undefined, { year: 'numeric',
      month: 'long', day: 'numeric' })}}
    </p>
  </a>
</li>
{%- endfor -%}

Make custom filter to use same formatting options across pages

This works in both places, but since we're using the same toLocaleString( undefined, { year: 'numeric',month: 'long', day: 'numeric' }) options in multiple places, it'd probably make more sense to make our own date filter.

In our .eleventy.js at the root of our 11ty project, we're going to use the addFilter function, like this:

module.exports = function (eleventyConfig) {
  // put your other configurations and functions alongside this one inside of `module.exports`
  // there can only be one `module.exports` inside of `.eleventy.js`
  eleventyConfig.addFilter("postDate", (dateObj) => {
    // Can use toLocaleString the same way we were before
    return dateObj.toLocaleString(undefined, {
      year: "numeric",
      month: "long",
      day: "numeric",
    });
  });
};

Then use the new filter we've made in our pages.

How we'd use this in _includes/post.njk:

<p>Last updated: {{page.date | postDate}}</p>

How we'd use this in posts/index.njk:

{%- for post in collections.post -%}
<li>
  <a href="{{ post.url }}">
    <h2>{{ post.data.title }}</h2>
    <p>Last updated: {{post.date | postDate}}</p>
  </a>
</li>
{%- endfor -%}