11.2 Whitespace Control: - in Actions
Right, let’s talk about whitespace. It’s the silent, invisible monster that will absolutely ruin your beautifully crafted HTML output, leaving you with a minified-looking mess or, worse, a single line of text that breaks your entire layout. Go’s template engine is notoriously, almost comically, enthusiastic about printing every single character you tell it to, including all the newlines and tabs you put in your template for readability. This is where the magic dash (-) inside your actions comes in. It’s not just a suggestion; it’s your primary weapon in the war for clean output.
Think of the spaces and newlines around your {{ }} actions as literal characters waiting to be printed. Because they are. Go templates don’t implicitly trim them; it assumes if you typed them, you meant them. This is a classic case of the designers being a bit too literal-minded.
The Anatomy of the Dash
The dash, -, is placed inside the action’s delimiters to eat the whitespace on that side. Its position is everything:
{{-(a dash on the left) trims the whitespace preceding the action.-}}(a dash on the right) trims the whitespace following the action.{{- -}}(dashes on both sides) trims all surrounding whitespace.
Let’s move from a horrifying example to a clean one. Here’s the problem, in all its ugly glory:
<!-- Without Whitespace Control -->
<h1>
{{ .Title }}
</h1>
<ul>
{{ range .Pages }}
<li>{{ .Title }}</li>
{{ end }}
</ul>
This will render something truly absurd like:
<h1>
My Great Page
</h1>
<ul>
<li>Post One</li>
<li>Post Two</li>
</ul>
See all those gaps? Those are the newlines and indentation from your template, rendered verbatim. It’s technically valid HTML, but it’s messy, unprofessional, and can interfere with things like inline block elements. Let’s fix it.
Taming the Beast with {{- and -}}
The goal is to produce clean, tight HTML. We use the dashes to strip away the formatting whitespace we needed, but that the browser doesn’t.
<!-- With Whitespace Control -->
<h1>
{{- .Title -}}
</h1>
<ul>
{{- range .Pages }}
<li>{{ .Title }}</li>
{{- end }}
</ul>
Now, look at that beautiful output:
<h1>My Great Page</h1>
<ul>
<li>Post One</li>
<li>Post Two</li>
</ul>
Perfectly clean. Let’s break down why:
{{- .Title -}}: The left dash eats the indentation space before the action, the right dash eats the newline after the action. The content of.Titleis snug inside the<h1>tags.{{- range .Pages }}: The left dash eats the newline and indentation before therangeaction, so the<ul>tag is immediately followed by our first<li>.{{- end }}: The left dash eats the newline and indentation before theendaction, ensuring the closing</ul>tag is on a new line but isn’t preceded by a bunch of empty space.
Common Pitfalls and the “Why”
The most common mistake is over-trimming. You have to remember that the dash is ruthless; it consumes all contiguous whitespace characters (spaces, tabs, newlines) until it hits a non-whitespace character. This can sometimes bite you.
{{- .Title }} is great! <!-- Output: "My Great Pageis great!" (Whoops, missing space) -->
{{ .Title -}} is great! <!-- Output: "My Great Page is great!" (Correct) -->
In the first line, the left dash {{- eats the space between the action and the word “is”. In the second line, the right dash -}} only eats the whitespace after the action, leaving the intended space before “is” intact.
Another edge case is when you need to preserve a space. Imagine a list of tags:
Tags: {{ range .Tags }}{{ . }}, {{ end }}
<!-- Output: "Tags: go, hugo, web, " --> <!-- Trailing comma, gross -->
You can use whitespace control with a conditional to fix this elegantly:
Tags: {{ range $index, $tag := .Tags }}{{ if $index }}, {{ end }}{{ $tag }}{{ end }}
<!-- Output: "Tags: go, hugo, web" -->
Here, {{ if $index }}, {{ end }} prints a comma and a space only for every element after the first one ($index is 0 for the first element). We don’t use dashes here because we need to precisely control the spaces around the commas ourselves.
The golden rule is this: Use whitespace control by default for block-level elements (div, p, ul, h1 etc.) to keep your HTML clean. Be more surgical and precise with your dashes for inline content where spaces have meaning. Always diff your built site’s HTML or use your browser’s “View Source” to check your work. It’s the only way to see the invisible monster you’re fighting.