Right, let’s talk about order. Your content is a pile of brilliant ideas, and by default, Hugo will list them in a way that makes sense only to a computer (alphabetical by filename, if you’re curious). We’re not computers. We want to control the narrative. Hugo gives you a delightful little toolbox of front matter fields to impose your will upon this chaos. Let’s break them down, from the most blunt instrument to the most nuanced.

The Blunt Instrument: weight

This is the old-school, classic way to order things. Think of it like setting a priority number. Lower numbers have higher priority and float to the top.

---
title: "My Most Important Post"
weight: 10
---
---
title: "A Slightly Less Important Post"
weight: 20
---
+++
title = "My Most Important Post"
weight = 10
+++
+++
title = "A Slightly Less Important Post"
weight = 20
+++

Why it works this way: It’s dead simple for Hugo. It just sorts all your pages by this integer field. The beauty is in its simplicity; the horror is in its maintenance. What happens when you write a new post that needs to slot between weight: 10 and weight: 20? You now have to go and re-number half your site. It’s a recipe for a migraine. Use weight for a few, critical, manually ordered pages (like a “Featured Projects” section). For a blog? There are better tools.

The Chronological Workhorses: date and publishDate

Hugo has a deeply ingrained, and frankly correct, assumption that most content is related to time. So its default sort order for regular pages is by date descending, putting the newest stuff first.

  • date: This is the big one. Hugo considers this the canonical date for the content. When you do range .Pages }}, they’re ordered by date by default. This is typically the date you wrote the thing.
  • publishDate: This is a more specific cousin. If you’re writing a post today but you don’t want it to go live until next week, you set publishDate in the future. Hugo’s built-in publishing logic uses this. Here’s the kicker: if publishDate is set, Hugo will use it instead of date for sorting. No, it’s not documented clearly enough, and yes, everyone is surprised by this the first time.
---
title: "Scheduled for the Future"
date: 2023-10-26T10:00:00Z
publishDate: 2023-11-01T09:00:00Z
---
+++
title = "Scheduled for the Future"
date = 2023-10-26T10:00:00Z
publishDate = 2023-11-01T09:00:00Z
+++

In the above example, until November 1st, this page will be considered “in the future” and might not appear in your list pages unless you configure Hugo to include future content. On November 1st, it will be sorted as if its date was 9:00 AM on the 1st, not 10:00 AM on the 26th.

Best Practice: Be consistent. Decide if you’re a date person or a publishDate person. I use date for the true creation date and only use publishDate when I’m actually scheduling something. This avoids the mental overhead of remembering which date Hugo is secretly using.

The Cleanup Crew: lastmod

This field is Hugo’s gift to your SEO team. lastmod stands for “last modified” and it’s exactly what it sounds like. You update a post two years later with a new code example? Bump the lastmod field.

---
title: "My Old But Updated Post"
date: 2021-05-15T08:00:00Z
lastmod: 2023-10-26T14:30:00Z
---
{
  "title": "My Old But Updated Post",
  "date": "2021-05-15T08:00:00Z",
  "lastmod": "2023-10-26T14:30:00Z"
}

Why it matters: By default, Hugo’s XML sitemap template uses this field to tell search engines when a page was last updated, which is a signal they love. You can also use it in your templates to proudly display “Updated on…” notices, which adds credibility. The sorting behavior for lastmod isn’t default, but it’s incredibly powerful. Want a “Recently Updated” sidebar? You sort by lastmod instead of date.

{{ range where .Site.Pages "Lastmod" "ge" (now.AddDate 0 -1 0) | first 5 }}
  <li><a href="{{ .Permalink }}">{{ .Title }} (Updated {{ .Lastmod.Format "Jan 2" }})</a></li>
{{ end }}

The Silent Bouncer: expiryDate

This one is hilariously direct. Set a date in the past for expiryDate, and Hugo will straight-up refuse to render the page. It returns a 404. It’s the digital equivalent of a bouncer looking at your ID, pointing to the door, and saying “not tonight, pal.”

---
title: "Post About Old Framework Version"
date: 2022-01-10T11:00:00Z
expiryDate: 2023-01-10T11:00:00Z
---
+++
title = "Post About Old Framework Version"
date = 2022-01-10T11:00:00Z
expiryDate = 2023-01-10T11:00:00Z
+++

The Pitfall: This is a nuclear option. It’s not a “hide from the list” option; it’s a “delete this from the universe” option. The content is gone. If you just want to remove something from your main blog feed but keep the URL accessible for legacy links, you’re looking for draft: true or a different publishing configuration. Use expiryDate when you are absolutely, positively sure you never want that page to see the light of day again after a certain date. It’s perfect for time-sensitive announcements or content that becomes legally problematic after a certain point.