Right, let’s get our hands dirty. You’ve probably noticed Hugo is fast, but maybe your site has grown, and that initial speed has started to feel a bit… theoretical. Before you start randomly tweaking things in a panic, you need to know what’s actually slow. Throwing --templateMetrics and --templateMetricsHints at Hugo is like switching from a polite conversation about the weather to getting a full diagnostic readout from a jet engine. It’s brutally honest, occasionally terrifying, and exactly what you need.

Here’s the command that pulls back the curtain:

hugo --templateMetrics --templateMetricsHints

Run this, let Hugo build your site, and then scroll up. You’ll be greeted by a table that looks like something a corporate accountant would dream up. It breaks down the time spent rendering every single template file, from layouts to partials to shortcodes, in order from most egregious time-sink to most innocuous.

What the Metrics Actually Mean

The table has a few key columns. cumulative is the big one—it’s the total time spent inside that template, including all the time spent in any other templates it calls. Think of it as the total cost of ownership. Then there’s average, which is the cumulative time divided by how many times the template was executed. This is crucial: a template with a high cumulative but a low average is called a lot (maybe inside a loop). A template with a high average is just intrinsically slow, like a heavy partial rendering a complex menu.

The hints column is where Hugo, in its own wonderfully passive-aggressive way, tries to give you a nudge. It’s not going to rewrite the code for you, but it will point out the obvious sins it sees. Which brings us to the main event.

Decoding the –templateMetricsHints

This flag is the real genius—and the source of most of the laughs. Hugo will analyze your template and, if it sees something particularly egregious, it will slap a hint right there in the table. These aren’t gentle suggestions; they’re accusations.

You’ll see classics like:

  • "[]range ... -> if len value: This is Hugo telling you you’re a fool for using range over a collection without first checking if it’s empty. If you have an empty slice, checking if .Pages is instant; blindly running range over it still has overhead. This one is almost always worth fixing.
  • "expensive partial call": This is the big one. It means you’re using partial and not partialCached. We’ll get to caching in a second, but this hint is Hugo screaming that you’re re-rendering the exact same chunk of HTML on every single page for no good reason.

The hints are brilliantly specific. It might call out "map read" or "map write" for operations on Go templates maps, which are notoriously slow compared to pre-computed data. It’s like having a grumpy senior developer looking over your shoulder, muttering “you’re gonna wanna fix that.”

A Real-World Example: The Culprit

Let’s say you run the command and see this line at the top of your report:

cumulativeaveragemaximumtemplate
4.2s21.4ms102.5mspartials/related-posts.html

Ouch. 4.2 seconds total on a related posts partial? And that hint is the smoking gun. You’re rendering it fresh on every page, probably doing a heavy lookup each time. The fix? Switch to partialCached. The first time Hugo builds a page with a specific set of parameters (like the current .Title and .Section), it renders the partial and stores the result. Every subsequent page that needs the same cached version just grabs the HTML blob. It’s cheating, and it’s glorious.

So you’d change this in your template:

{{/* Slow and naughty */}}
{{ partial "related-posts.html" . }}

To this:

{{/* Fast and virtuous */}}
{{ partialCached "related-posts.html" . .Title .Section }}

The cache key (the bits after the context .) is vital. It needs to be unique for each variation of the output you need. Using the .Title and .Section means you’ll get a unique cache for each post within a section, which is probably what you want.

Best Practices and Pitfalls

First, always run this with --gc (garbage collection). A full build without cleaning up first can give you misleading numbers because of leftover files. hugo --gc --templateMetrics --templateMetricsHints is the professional’s choice.

Second, ignore the noise on tiny sites. If your site builds in 300ms, this table is just a curiosity. Save it for when builds creep into the 10s+ range.

The biggest pitfall is misusing partialCached. Your cache key must account for every variable that affects the partial’s output. If your “related posts” partial also depends on tags, but you only keyed it on the title, a post with the same title but different tags would serve the wrong cached HTML. You have to understand your own templates to cache them safely. There’s no free lunch, but --templateMetricsHints absolutely buys you dinner.