11.4 Conditionals: if, else, else if
Right, conditionals. This is where your templates stop being dumb document stampers and start making actual decisions. It’s the “if this, then that” logic that makes your site dynamic. Go templates give you if, else if, and else. The syntax is blessedly straightforward, but the devil, as always, is in the details.
Let’s start with the basic structure. It looks a lot like most programming languages, just with those mustache-like braces.
{{ if .IsHome }}
<h1>Welcome to the Madness!</h1>
{{ else }}
<h1>{{ .Title }}</h1>
{{ end }}
See? No surprises. You start with if, then a condition, then the content to render if that condition is true. You close the whole party with an end. The else is optional and sits in the middle. The condition can be any expression that evaluates to a boolean (true/false) value.
The Concept of “Truthiness” in Go
This is the most critical thing to grok. Go templates have a very specific, and sometimes surprising, definition of what is “true” for an if statement. It’s not just about true and false. It’s about whether a value exists, is non-empty, or is non-zero. This is called “truthiness.”
A condition is considered false if the value is:
false(the boolean, obviously)- Any numeric zero value (
0,0.0) - A nil pointer or interface
- An empty array, slice, map, or string
- A
time.Timezero value
Everything else is true. This is incredibly powerful. It means you can check for the existence of data without doing tedious length checks.
{{ if .Params.tags }}
<div class="tags">
<strong>Tagged with:</strong>
{{ range .Params.tags }}
<span>{{ . }}</span>
{{ end }}
</div>
{{ end }}
In this example, {{ if .Params.tags }} is false if the tags field in the front matter doesn’t exist, is an empty array [], or is null. It’s true if there are any tags in the array. This is so much cleaner than writing if len(.Params.tags) > 0.
Nesting and else if Ladders
You can, of course, get more complex. You can nest if statements inside each other, and you can chain conditions with else if.
{{ if eq .Kind "page" }}
<p>This is a single page.</p>
{{ else if eq .Kind "section" }}
<p>This is a section listing.</p>
{{ else if eq .Kind "home" }}
<p>This is the freakin' homepage!</p>
{{ else }}
<p>What even is this? Kind: {{ .Kind }}</p>
{{ end }}
This works exactly as you’d expect: it checks each condition in order and executes the block for the first one that’s true. The else is the catch-all. Pro tip: be mindful of the order. If you put the home page check first in a different scenario, it might trigger instead of a more specific condition.
The With-If Hybrid and Why I Love It
Here’s a fantastically useful pattern that combines with (which is just a fancy if that scopes the context) and a conditional. Let’s say you have an optional author image in your front matter.
{{/* The clunky way */}}
{{ if .Params.author_image }}
<img src="{{ .Params.author_image }}" alt="Author Image" class="avatar">
{{ end }}
{{/* The elegant, "with-as-if" way */}}
{{ with .Params.author_image }}
<img src="{{ . }}" alt="Author Image" class="avatar">
{{ end }}
The with block here is functionally identical to the if block. It checks the truthiness of .Params.author_image. If it exists and isn’t empty, it sets the context inside the block to that value (so . becomes the image path). It’s cleaner and scopes your context perfectly. Use this. It makes your templates neater.
Common Pitfalls and How to Faceplant Gracefully
Trying to Compare Entirely Different Types: Go is strictly typed, even in templates.
{{ if eq .Title 42 }}will fail spectacularly because you’re comparing a string to an integer. The template will flat-out refuse to render. You have to compare like with like.Overlooking Operator Precedence: Want to check if two things are both true? You need to use the
andfunction. But note: you can’t doif eq .A .B and eq .C .D. The parser will get confused. You have to wrap the individual conditions in parentheses. It’s a bit ugly, but it works.{{/* This will likely cause an error */}} {{ if eq .Type "post" and .IsPage }} {{/* Do this instead */}} {{ if (eq .Type "post") and (.IsPage) }}Forgetting the
end: You will do this. I still do this. You’ll add anifstatement, get distracted by a squirrel, and then spend ten minutes wondering why your entire template is broken. Always double-check that your{{ end }}matches your{{ if }}. Good code formatting (like indentation) is your best friend here.
The designers got a lot right here. The truthiness concept is a stroke of genius that eliminates tons of boilerplate code. The syntax is clean and readable. The main questionable choice is the sometimes-verbose function syntax (eq .A .B instead of .A == .B), but you get used to it. It’s a small price to pay for the power and flexibility you get. Now go make your templates smart.