11.7 define and block: Named Templates
Now, let’s get into the good stuff: making your templates modular and reusable. You’ve met {{ define }} and {{ template }}, which are the basic building blocks. But what happens when you want to define a default version of a block that can be optionally overridden by a more specific template? You can’t just {{ define }} the same thing twice; that’s a one-way ticket to a template compilation error. This is where {{ block }} comes in, and it’s Hugo’s secret weapon for creating flexible, extensible layouts without the copy-paste madness.
Think of {{ block }} as a friendly, “unless you have something better” statement. You define a placeholder in a base template, along with a default implementation right there. If a child template provides its own definition for that block name, its version wins. If it doesn’t, the default just hums along quietly. It’s the Go template version of a sensible default value.
The Anatomy of a block
Here’s the basic syntax. You use it in your base template (e.g., baseof.html).
<!-- layouts/_default/baseof.html -->
<!DOCTYPE html>
<html>
<head>
<title>{{ block "title" . }}My Cool Site - {{ .Title }}{{ end }}</title>
</head>
<body>
{{ block "header" . }}
<header><h1>Default Header</h1></header>
{{ end }}
<main>
{{ block "main" . }}
<p>The main content should go here.</p>
{{ end }}
</main>
{{ block "footer" . }}
<footer>Default footer content. © 2024</footer>
{{ end }}
</body>
</html>
Notice the dot (.) passed as the second argument to the {{ block }}? That’s crucial. It passes the current context (the page data, the site data, etc.) into the block. If you forget that dot, the block’s content will be rendered with a nil or unexpected context, and you’ll be staring at a blank page wondering what you did to deserve this. It’s the most common pitfall, so I always double-check it.
Overriding a Block in a Specific Template
Now, in a specific template (say, for a single post), you don’t {{ define }} the whole layout from <html> to </html>. That would defeat the entire purpose of having a base template. Instead, you only {{ define }} the blocks you want to change.
<!-- layouts/_default/single.html -->
{{ define "title" }}
{{ .Title }} | A Much Better Site Name
{{ end }}
{{ define "main" }}
<article class="post">
<h1>{{ .Title }}</h1>
<div class="content">
{{ .Content }}
</div>
</article>
{{ end }}
Here’s the magic: when Hugo renders single.html, it uses baseof.html as the foundation. It sees the {{ block "main" }} and checks if the calling template (single.html) has defined a “main” block. It has! So it uses that gorgeous <article> code instead of the boring default <p> tag. It does the same for the “title” block. For the “header” and “footer” blocks, which we didn’t redefine, it just happily uses the defaults from the base template. Elegant, isn’t it?
The Subtle But Critical Difference
This is where people’s brains sometimes glitch. {{ define }} and {{ block }} are not interchangeable.
- You
{{ define "something" }}in a template that is meant to be executed with{{ template "something" }}or as an override for a{{ block "something" }}. It’s a definitive declaration. - You
{{ block "something" }}in a parent template to specify a placeholder and a default. It’s an invitation to override.
A template file that only contains {{ define }} statements is not a standalone template; it’s a library of components meant to be pulled into another template (usually a base template using {{ block }}).
Best Practices and Power Moves
The Non-Negotiable Main Block: Your
baseof.htmlshould almost always have a{{ block "main" . }}{{ end }}block, even if the default is empty. This is the conduit through which all your page content flows. It’s the one block every template must define.Context is King (Again): I can’t stress this enough. Always pass the context:
{{ block "your_block_name" . }}...{{ end }}. If the content inside your block needs to access page variables like.Titleor.Content, that dot is their lifeline. No dot, no data.Scoped Blocks: You can create blocks for smaller components, not just major layout sections. Need a customizable sidebar in a section?
{{ block "sidebar" . }}...{{ end }}. This keeps your base template clean and your options open.
This system is one of Hugo’s finest features. It forces a clean separation of concerns between the global structure of your site and the specific content of your pages. Once you get the hang of block and define, you’ll start structuring all your templates this way, and you’ll wonder how you ever managed without it.