Question

How to Pretty-Print JSON in Go

gojsonpretty-print

Question

How can I pretty-print JSON output in Go?

I want a simple way to format JSON so it is easier to read in Go. Specifically, I would like to:

  • pretty-print the result of json.Marshal
  • format an existing JSON string so it becomes indented and readable

What is the standard way to do this in Go?

Short Answer

By the end of this page, you will understand how to format JSON in Go using the standard library. You will learn when to use json.MarshalIndent for Go values, when to use json.Indent for existing JSON text, and how to avoid common mistakes when working with indented output.

Concept

JSON can be stored or transmitted in two common forms:

  • compact JSON: no extra spaces or line breaks
  • pretty-printed JSON: includes indentation and new lines for readability

In Go, the encoding/json package gives you built-in tools for both cases.

Two common situations

  1. You have a Go value such as a struct, map, or slice, and want JSON output.

    • Use json.MarshalIndent
  2. You already have JSON text in a string or byte slice and want to reformat it.

    • Use json.Indent

Why this matters

Pretty-printing JSON is useful when:

  • debugging API responses
  • logging structured data
  • saving readable configuration files
  • inspecting test output
  • making JSON easier for humans to review

Pretty-printing does not change the actual data. It only changes how the JSON text is laid out with spaces and new lines.

Go keeps this simple by separating:

  • encoding Go data into JSON with Marshal and MarshalIndent
  • reformatting existing JSON text with Indent

That distinction is important because the right function depends on what you start with.

Mental Model

Think of JSON formatting like writing an address.

  • Compact JSON is like writing the whole address on one line.
  • Pretty-printed JSON is like splitting it across multiple lines so it is easier to scan.

The information is the same either way.

In Go:

  • json.MarshalIndent is like taking your data and writing it neatly from the start.
  • json.Indent is like taking a messy block of text and reformatting it cleanly.
Take Quiz

Syntax and Examples

Pretty-print a Go value with json.MarshalIndent

Use this when you have a Go value such as a struct or map.

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    Name  string `json:"name"`
    Age   int    `json:"age"`
    Admin bool   `json:"admin"`
}

func main() {
    user := User{Name: "Ava", Age: 28, Admin: true}

    data, err := json.MarshalIndent(user, "", "  ")
    if err != nil {
        fmt.Println("error:", err)
        return
    }

    fmt.Println(string(data))
}

Output:

{
  "name": "Ava",
  "age": 28,
  "admin": true
}

What the arguments mean

json.MarshalIndent(v, prefix, indent)
  • v: the Go value to encode
  • prefix: text added at the start of each line, often ""
  • indent: the indentation string for each nesting level, often " " or "\t"

Format existing JSON text with json.Indent

Use this when you already have JSON in a string or []byte.

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
)

func main() {
    raw := `{"name":"Ava","age":28,"skills":["Go","SQL"]}`

    var out bytes.Buffer
    err := json.Indent(&out, []byte(raw), "", "  ")
    if err != nil {
        fmt.Println("error:", err)
        return
    }

    fmt.Println(out.String())
}

Output:

{
  "name": "Ava",
  "age": 28,
  "skills": [
    "Go",
    "SQL"
  ]
}

When to choose which

  • Use json.MarshalIndent if you start with a Go value
  • Use json.Indent if you start with JSON text

Step by Step Execution

Consider this example:

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    m := map[string]any{
        "name": "Lina",
        "age":  31,
    }

    data, err := json.MarshalIndent(m, "", "  ")
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println(string(data))
}

Step by step

  1. m := map[string]any{...}

    • A Go map is created with two key-value pairs.
  2. json.MarshalIndent(m, "", " ")

    • Go converts the map into JSON.
    • "" means no extra prefix is added to each line.
    • " " means each nested level uses two spaces.
  3. The result is stored in data

    • data is a []byte, not a string.
  4. if err != nil

    • If encoding fails, the program prints the error and stops.
  5. fmt.Println(string(data))

    • The byte slice is converted to a string and printed.

Result

{
  "age": 31,
  "name": "Lina"
}

Note that map key order should not be relied on for program logic. The printed order may differ from how you wrote the map.

Real World Use Cases

Common uses for pretty-printed JSON

Debugging API responses

When calling an API, compact JSON can be hard to read in logs. Pretty-printing helps you inspect nested objects and arrays.

Writing readable config files

If your Go program saves settings as JSON, indented output makes the file easier for humans to edit.

Test output

When a test fails, pretty JSON is much easier to compare than one long line.

Logging structured data during development

During local development, developers often print indented JSON to understand what data is flowing through the program.

Data inspection tools

CLI tools built in Go may fetch or generate JSON and present a readable version to the user.

Take Quiz

Real Codebase Usage

In real Go projects, developers usually combine pretty-printing with a few common patterns.

1. Pretty-print only in development or debugging

Indented JSON is larger than compact JSON, so many applications avoid it in production responses unless readability matters.

if debug {
    data, _ := json.MarshalIndent(payload, "", "  ")
    fmt.Println(string(data))
}

2. Return early on JSON errors

Error handling is usually done immediately.

data, err := json.MarshalIndent(payload, "", "  ")
if err != nil {
    return err
}

3. Validate incoming JSON before formatting it

If JSON comes from outside your program, it may be invalid.

var out bytes.Buffer
if err := json.Indent(&out, input, "", "  "); err != nil {
    return fmt.Errorf("invalid JSON: %w", err)
}

4. Use helper functions

Teams often wrap formatting in a reusable function.

func PrettyJSON(v any) (string, error) {
    data, err := json.MarshalIndent(v, "", "  ")
    if err != nil {
        return "", err
    }
    return string(data), nil
}

5. Keep API responses compact unless readability is required

For machine-to-machine communication, compact JSON is often preferred to reduce size.

Common Mistakes

1. Using json.Marshal when you actually want indented output

Broken example:

data, err := json.Marshal(user)
fmt.Println(string(data))

This works, but the output is compact, not pretty.

Correct version:

data, err := json.MarshalIndent(user, "", "  ")
fmt.Println(string(data))

2. Trying to pass a JSON string directly to json.MarshalIndent

Broken example:

raw := `{"name":"Ava"}`
data, _ := json.MarshalIndent(raw, "", "  ")
fmt.Println(string(data))

This encodes the string itself as JSON, which is not the same as reformatting the JSON text.

Correct approach:

var out bytes.Buffer
err := json.Indent(&out, []byte(raw), "", "  ")
fmt.Println(out.String())

3. Forgetting to handle errors

Broken example:

data, _ := json.MarshalIndent(ch, "", "  ")
fmt.Println(string(data))

Some Go values cannot be encoded as JSON, such as channels or functions.

Better:

data, err := json.MarshalIndent(ch, "", "  ")
if err != nil {
    fmt.Println("error:", err)
    return
}

4. Forgetting that json.Indent writes into a buffer

json.Indent does not return formatted bytes directly. It writes to an io.Writer, often a bytes.Buffer.

Correct pattern:

var out bytes.Buffer
err := json.Indent(&out, input, "", "  ")

5. Assuming pretty-printing changes the data

Formatting changes whitespace only. It does not change keys, values, types, or structure.

Comparisons

TaskBest FunctionInputOutput
Encode a Go struct/map/slice as compact JSONjson.MarshalGo value[]byte
Encode a Go struct/map/slice as readable JSONjson.MarshalIndentGo value[]byte
Reformat existing JSON textjson.IndentJSON []byte + writerformatted text in buffer

json.Marshal vs json.MarshalIndent

FunctionUse it whenIndented output?
json.MarshalYou want compact JSONNo
json.MarshalIndentYou want human-readable JSONYes

json.MarshalIndent vs json.Indent

FunctionStart withMain purpose
json.MarshalIndentA Go valueEncode and indent JSON
json.IndentExisting JSON textReformat valid JSON text

Spaces vs tabs for indentation

Indent stringExampleNotes
" "two spacesvery common and readable
"\t"tabshorter to type, style preference
" "four spacesmore spread out

Choose based on project style or readability needs.

Take Quiz

Cheat Sheet

Quick reference

Pretty-print a Go value

data, err := json.MarshalIndent(v, "", "  ")

Pretty-print existing JSON text

var out bytes.Buffer
err := json.Indent(&out, input, "", "  ")
formatted := out.String()

Signatures

json.MarshalIndent(v any, prefix, indent string) ([]byte, error)
json.Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error

Use this when

  • json.MarshalIndent: you have a Go value
  • json.Indent: you already have JSON text

Common indent values

""    // no prefix
"  "  // two spaces
"\t"  // tab

Important rules

  • Pretty-printing changes formatting, not data
  • Always check errors
  • json.MarshalIndent returns []byte
  • json.Indent writes into a buffer
  • Invalid JSON input causes json.Indent to return an error

Common pitfall

raw := `{"x":1}`
json.MarshalIndent(raw, "", "  ")

This encodes a string, not the JSON structure inside the string.

FAQ

How do I pretty-print JSON from a struct in Go?

Use json.MarshalIndent:

data, err := json.MarshalIndent(v, "", "  ")

How do I format an existing JSON string in Go?

Use json.Indent with a bytes.Buffer:

var out bytes.Buffer
err := json.Indent(&out, []byte(raw), "", "  ")

What is the difference between json.Marshal and json.MarshalIndent in Go?

json.Marshal creates compact JSON. json.MarshalIndent creates indented, human-readable JSON.

Does pretty-printing JSON change the data?

No. It only changes whitespace such as spaces and line breaks.

Why does json.MarshalIndent on a string not format my JSON text?

Because it treats the string as a plain Go string value and encodes that string into JSON. It does not parse and reformat the JSON inside the string.

Can json.Indent fail?

Yes. It returns an error if the input is not valid JSON.

Should I use pretty-printed JSON in production APIs?

Usually compact JSON is preferred for smaller payloads, but pretty JSON is useful for debugging, tools, and human-edited files.

Take Quiz

Mini Project

Description

Build a small Go program that prints a compact JSON payload in a readable format. This project demonstrates both common pretty-printing workflows: creating readable JSON from a Go struct and reformatting an existing JSON string.

Goal

Create a program that pretty-prints JSON from both a Go value and an existing raw JSON string.

Requirements

  • Define a struct representing a simple API response or user record.
  • Use json.MarshalIndent to print the struct as indented JSON.
  • Store a compact JSON string and reformat it with json.Indent.
  • Print both formatted results to the console.
  • Handle any errors returned by JSON functions.
Take Quiz

Keep learning

Related questions

View more