When working with Go’s text/template package, formatting nested data structures often results in unwanted blank lines. Because Go templates preserve all whitespaceโincluding the newlines used to format the template code itselfโstandard loops produce bloated output.
Here is how to solve the whitespace problem using Go’s template trim markers.
Say we want to format this nested data structure:
var data = map[string]map[string][]string{
"1": {
"1.1": {"1.1.1", "1.1.2", "1.1.3"},
},
"2": {
"2.1": {"2.1.1", "2.1.2", "2.1.3"},
},
}
A natural way to write the template is to use standard {{ range }} blocks:
{{ range $name, $map := . }}
{{ $name }}:
{{ range $name, $values := $map }}
{{ $name }}:
{{ range $value := $values }}
{{ $value }}
{{ end }}
{{ end }}
{{ end }}
Because every newline and indentation space in the template is printed exactly as written, the output is littered with blank spaces:
1:
1.1:
1.1.1
1.1.2
1.1.3
2:
...
-)
๐You can instruct the Go template engine to consume adjacent whitespace by adding a minus sign (-) inside the action tags:
{{- ... }} removes all preceding whitespace (spaces, tabs, and newlines) up to the previous non-whitespace character.{{ ... -}} removes all following whitespace up to the next non-whitespace character.To fix our nested loop, we strategically place the - marker to eat the template’s formatting newlines without removing our intended indentation:
{{- range $name, $map := . -}}
{{ $name }}:
{{- range $name, $values := $map }}
{{ $name }}:
{{- range $value := $values }}
{{ $value }}
{{- end }}
{{- end }}
{{ end -}}
By stripping the newlines immediately following the range declarations and the end blocks, we get clean, tightly packed output:
1:
1.1:
1.1.1
1.1.2
1.1.3
2:
2.1:
2.1.1
2.1.2
2.1.3
You can copy and run this code to test the behavior directly.
package main
import (
"fmt"
"os"
"text/template"
)
var data = map[string]map[string][]string{
"1": {
"1.1": {"1.1.1", "1.1.2", "1.1.3"},
},
"2": {
"2.1": {"2.1.1", "2.1.2", "2.1.3"},
},
}
func main() {
const tpl = `
{{- range $name, $map := . -}}
{{ $name }}:
{{- range $name, $values := $map }}
{{ $name }}:
{{- range $value := $values }}
{{ $value }}
{{- end }}
{{- end }}
{{ end -}}
`
fmt.Println("Output:")
fmt.Println("'''")
t := template.Must(template.New("nested").Parse(tpl))
t.Execute(os.Stdout, data)
fmt.Println("'''")
}
Get the curated offline edition of Kaue's Way of Lifeโa practical field guide to optimizing ๐ Health, ๐ฐ Wealth, โค๏ธ Love, and ๐ง Meaning.
Get notified when I publish new essays, book summaries, and practical guides on living well. No spamโunsubscribe anytime.
Have a question, perspective, or suggestion? Share your thoughts below.
Updated on 2026 Oct 11.
DISCLAIMER: The content on this site is for informational and educational purposes only and does not constitute medical, psychological, financial, investment, tax, or legal advice. The ideas, curation, and opinions presented here are my own and do not represent those of my employer. Book, lecture, and article summaries are independent educational commentary and remain the intellectual property of their respective authors. AI tools may be used to assist with editing, formatting, and synthesis; while every effort is made to ensure accuracy, errors may occur. PRIVACY: This site uses third-party services for cookieless analytics (Google Analytics), optional newsletter subscriptions (Mailchimp), comments (Disqus), live chat (Tawk.to), and embedded media (YouTube), which may process visitor IP addresses and cookies in accordance with their respective privacy policies.