14.2 Calling Partials: partial and partialCached
Alright, let’s talk about getting these partial templates onto your page. You’ve defined these reusable fragments, these beautiful little components of HTML, and now you need to actually, you know, use them. Hugo gives you two main ways to do this: partial and partialCached. One is your reliable workhorse, the other is a performance-obsessed specialist. Choosing the right one is the difference between a swift, elegant site and one that’s constantly doing unnecessary heavy lifting.
The Workhorse: partial
The partial function is straightforward. You call it, it finds your template, renders it with the context you provide, and spits out the HTML. It’s your go-to for most situations. The syntax is simple:
<!-- layouts/_default/baseof.html -->
<body>
<header>
{{ partial "header.html" . }}
</header>
<main>
{{ block "main" . }}{{ end }}
</main>
<footer>
<!-- You can pass any data structure, not just the global context -->
{{ partial "site-footer.html" (dict "year" now.Year "author" "Me") }}
</footer>
</body>
See that? In the first example, we’re passing the entire current context (that dot .) to header.html. Maybe the header needs access to the site’s .Title or .Params. In the second, we’re being more surgical. We’re constructing a tiny dictionary (dict) of just the values our site-footer.html partial needs—the current year and an author name. This is a best practice. Don’t be lazy and pass the kitchen sink if your partial only needs a single spoon. It makes your templates more predictable and easier to reason about.
The Specialist: partialCached
Now, let’s say you have a partial that does something computationally expensive. Maybe it processes a giant list of tags, makes an external API call (though you should probably do that in a data file), or has some complex logic. Rendering it fresh on every single page where it appears is a waste of precious milliseconds. Enter partialCached.
<!-- Renders the partial once per section, caches the result -->
<aside>
{{ partialCached "section-related.html" . .Section }}
</aside>
<!-- Renders the partial once per page, caches the result -->
<aside>
{{ partialCached "page-related.html" . .RelPermalink }}
</aside>
The magic is in the extra arguments. The first argument after the partial name is the context you pass to it (just like a regular partial). Every argument after that is used to form a unique cache key. Hugo calculates the partial once for each unique combination of these cache key arguments and then just reuses the rendered HTML output on subsequent calls.
Why this is brilliant: That {{ partialCached "section-related.html" . .Section }} line means it will render the partial once for, say, your “/posts/” section, and then serve that cached version to every single page within /posts/. The performance gain can be massive.
The Pitfalls: When Caching Bites Back
Here’s where the designers’ questionable choice of a default behavior comes in. If you don’t provide a custom cache key, partialCached defaults to only one cache key: the variation in the passed context. This is almost never what you want. Look at this:
<!-- DANGER: Default cache key behavior -->
{{ partialCached "risky-partial.html" . }}
This will render and cache the partial once for the entire build. It will use that same cached HTML on every single page where this call appears. If your risky-partial.html outputs content that should be different from page to page (like a <title> tag containing .Title), you’ve just introduced a devastating bug where every page has the same title. Never, ever use partialCached without explicit cache keys unless you are absolutely certain the output should be identical across every page on your site. This is the most common and最容易踩的坑 (most easy-to-step-in pitfall) with this function.
Best Practices: Workhorse vs. Specialist
So, when do you use which?
- Use
partialby default. For your headers, footers, and most components. It’s safe and predictable. - Use
partialCachedfor expensive, context-aware partials that are called many times but whose output only changes based on specific, discrete factors. A sidebar of section-specific links is a perfect candidate. A component that depends on the unique.Titleof every page is not. - Always, always provide explicit cache keys for
partialCached. Use.Section,.Type,.RelPermalink, or a.Paramvalue—something that clearly defines the scope of the cache. Your future self, debugging at 2 AM, will thank you. - If your partial needs no context, you can pass
niland still use a cache key:{{ partialCached "footer.html" nil "static" }}. This is perfectly valid and very clear.