5.7 Weight and Ordering: Controlling Page Order in Lists
Right, let’s talk about the one thing Hugo thinks it can solve with a single number but which, in practice, requires a bit more finesse: ordering your content. You’ve got a list of pages—say, your blog posts or project portfolio—and by default, Hugo just slaps them on the page in chronological order. Sometimes that’s fine. Often, it’s anarchy. You want control. You want to say, “This one goes first, then that one, then the weird one from 2015 that’s suddenly relevant again.”
Hugo’s answer is the weight front matter field. It’s a simple concept: the higher the weight, the more “important” it is, and thus, the earlier it appears in a descending-order list. A page with weight 10 will appear before a page with weight 5. It’s like a bouncer at a club, but for your web pages.
The Absolute Basics of Weight
Let’s start with the simplest, most blunt-force use case. You explicitly assign a number. It’s not glamorous, but it works.
# content/posts/important-post.md
---
title: "The Most Important Post"
weight: 100
date: 2022-10-01
---
# content/posts/less-important-post.md
---
title: "A Less Important Post"
weight: 1
date: 2022-10-02
---
In your template, using {{ range .Pages.ByWeight }} would list the “Less Important Post” (weight: 1) first, followed by the “Most Important Post” (weight: 100). Wait, that’s ascending. See? It’s already slightly counterintuitive. You almost always want descending order, which is why you should almost never use .ByWeight. Instead, use .ByWeight.Reverse or, my strong preference, lean on the default behavior of the .RegularPages and .Pages objects within a section. They automatically use weight first, then date, in descending order. So a page with a higher weight will float to the top. This is a crucial bit of Hugo-nature to internalize.
The Pitfalls of Manual Weighting
Assigning numbers manually is a recipe for future frustration. It’s the software equivalent of naming your files Final_v2_ACTUAL_FINAL.docx. What happens when you need to slot a new, semi-important post between the one with weight 10 and the one with weight 11? You now have to renumber half your content. This is where the designers made a questionable choice by not building in a more dynamic system from the start. It’s a bit primitive.
A much more maintainable approach is to use a logical, constant scale. I think in tiers: 1000 for “landing page hero content,” 100 for “major feature,” 10 for “standard blog post,” and 1 for “meh, but it should still be there.” This gives you plenty of integers to play with in between if you need to get granular.
Automating This Madness: weight: date
Manually setting weights for every single page is for masochists. Let’s automate. A fantastic trick is to use the weight field to capture the publish date. This gives you reverse-chronological order (newest first) but using the weight field, which means it will intermingle properly with your manually weighted pages.
# content/posts/a-post.md
---
title: "A Post"
weight: 20221002
date: 2022-10-02
---
By formatting the date as YYYYMMDD, you get a number that naturally sorts correctly. A post from tomorrow (20221003) will have a higher number than today’s post (20221002) and will therefore appear before it in the default descending order. It’s clean, it’s logical, and it’s easily automated with your archetypes.
The Secret Weapon: _index.md and Section Home Pages
Here’s a pro move that often gets missed. What if you want to order entire sections? You can, and you do it the exact same way: by applying a weight to the section’s own _index.md file. This is how you control the order of, say, your “Projects,” “About,” and “Blog” links in your main navigation if you’re using section lists for that.
# content/projects/_index.md
---
title: "Projects"
weight: 200
---
# content/blog/_index.md
---
title: "Blog"
weight: 100
---
In this case, the “Blog” section (weight 100) would appear before the “Projects” section (weight 200) in a descending-weight list. This is incredibly powerful for structuring your site’s information architecture without hard-coding menus.
The Final Arbiter: Default Ordering
Remember, Hugo has a fallback hierarchy. When you call .Pages in a template, here’s the decision tree it follows to order things:
- Weight: This is the first-class citizen. Higher weight = higher precedence.
- Date: If two pages have the same weight (or none at all), it uses the date. Newer dates are considered “higher.”
- LinkTitle: If, by some miracle, two pages were created at the exact same millisecond and have the same weight, it falls back to alphabetizing by the title. This is the tie-breaker of last resort.
This hierarchy is why mixing manually weighted pages with weight: date pages works so well. Your “featured” post with weight: 999 will always trump a regular post with a weight of 20221002 (which is ~20 million), because Hugo checks the weight field first and doesn’t even get to the date comparison. It’s not a sum; it’s a sorted list of tuples: (weight, date, title). Understanding this underlying data structure is what separates you from the person who just copies configs from a forum post. You’re not just following instructions anymore; you’re reasoning about them.