๐Ÿ“ Controlling Whitespace in Go Templates: Nested Loops ๐Ÿ”—

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.

๐Ÿ“‘ Table of Contents ๐Ÿ”—


โš ๏ธ The Problem: Preserved Newlines ๐Ÿ”—

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:
...

โœ‚๏ธ The Solution: The Trim Marker (-) ๐Ÿ”—

You can instruct the Go template engine to consume adjacent whitespace by adding a minus sign (-) inside the action tags:

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

๐Ÿ’ป Full Runnable Example ๐Ÿ”—

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("'''")
}

๐Ÿ“š Download My Free Ebook ๐Ÿ”—

Get the curated offline edition of Kaue's Way of Lifeโ€”a practical field guide to optimizing ๐Ÿ Health, ๐Ÿ’ฐ Wealth, โค๏ธ Love, and ๐Ÿง˜ Meaning.

Kaue's Way of Life

Kaue's Way of Life
Minimum price
Free!
Suggested price
$0.00

๐Ÿ“ฌ Join My Mailing List ๐Ÿ”—

Get notified when I publish new essays, book summaries, and practical guides on living well. No spamโ€”unsubscribe anytime.


๐Ÿ’ฌ Comments & Discussion ๐Ÿ”—

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.