How do you make JSON.stringify() produce indented, human-readable output, and what is the middle argument doing in a call like JSON.stringify(value, null, 2)?
answer
- it is the third argument, not the second
- null occupies the slot in between
- number of spaces or an indent string
- the engine caps the indent at ten
- whitespace is invisible to the parser
basics
~10 sJSON.stringify's third parameter, space, controls indentation: pass a number of spaces or an indent string, as in JSON.stringify(value, null, 2). The null is the unused replacer slot sitting between the value and space.
solid answer
~50 s`JSON.stringify` takes three parameters: the value, a **replacer**, and **space**. Indentation is the third one, so to pretty-print you have to fill the replacer slot with something inert — conventionally `null` — and pass the indent as the third argument: `JSON.stringify(value, null, 2)`. `space` accepts either a number, meaning that many spaces per level, or a string used literally as the indent unit, so `'\t'` gives tab-indented output. Both forms are capped: a number is clamped to 10, and only the first 10 characters of a string are used. With no `space` the output has no whitespace at all; with a non-empty `space` the engine adds newlines, per-level indentation, and a space after each colon. Indentation is purely cosmetic — `JSON.parse` ignores whitespace between tokens, so pretty and compact text parse to exactly the same value.
code
javascript · 26 linesconst config = { name: 'svc', ports: [80, 443], tls: {} };
console.log(JSON.stringify(config));
// {"name":"svc","ports":[80,443],"tls":{}}
console.log(JSON.stringify(config, null, 2));
// {
// "name": "svc",
// "ports": [
// 80,
// 443
// ],
// "tls": {}
// }
// A string space is used literally as the indent unit.
console.log(JSON.stringify(config, null, '\t').split('\n')[1]);
// \t"name": "svc",
// Numeric space is clamped at 10.
console.log(JSON.stringify(config, null, 20) === JSON.stringify(config, null, 10));
// true
// Whitespace is not data: both texts parse to the same value.
console.log(JSON.stringify(JSON.parse(JSON.stringify(config, null, 2))));
// {"name":"svc","ports":[80,443],"tls":{}}go deeper
Remember the exact idiom JSON.stringify(value, null, 2) and be able to say that the third argument is the indent and the null is just holding the slot in between.
Explain the mechanics: space takes a number of spaces or a literal indent string, both capped at 10, and a non-empty value also adds newlines and a space after each colon.
Show judgment about where indentation belongs — pretty in repository files, fixtures and human-facing dumps because diffs stay readable; compact on wire payloads and high-volume logs because whitespace is pure byte cost.
Own the convention across a codebase: one indent style for committed JSON so diffs stay reviewable, compact serialization on every machine-to-machine path, and a rule about where formatting happens so the same document is not reserialized twice.
## The signature is the whole answer `JSON.stringify` accepts up to three arguments: ```js JSON.stringify(value, replacer, space) ``` The first is the value to serialize. The second is the **replacer** slot — a hook for filtering or transforming what gets written. The third is **space**, which controls indentation. Because JavaScript has positional arguments, you cannot skip the middle one: to reach `space` you must pass *something* for `replacer`. The idiom is `null`, which means "no replacer, serialize normally". Passing `undefined` works identically; `null` is simply the convention people recognise on sight. ```js JSON.stringify(obj, null, 2); // pretty, two-space indent ``` ## What space accepts **A number.** The value is truncated to an integer and clamped to a maximum of 10; the engine then indents each nesting level by that many spaces. A value of 0 or less — or a fractional value below 1 — means no indentation at all, identical to omitting the argument. ```js JSON.stringify(o, null, 2); // two spaces per level JSON.stringify(o, null, 20); // 10 spaces — clamped, not 20 JSON.stringify(o, null, 0); // compact, same as omitting space ``` **A string.** The string is used literally as the indent unit, and only its first 10 characters are taken. `'\t'` is the common choice for tab-indented files; a string of dots or vertical bars also works, which is occasionally handy when eyeballing deep structures. ```js JSON.stringify(o, null, '\t'); JSON.stringify(o, null, '....'); ``` **Anything else** is ignored, and you get compact output. (`Number` and `String` wrapper objects are unwrapped first and behave like their primitives; every other value simply has no effect.) ## What changes in the output With no `space`, the output contains no whitespace whatsoever — not even after the colons or commas: ```js JSON.stringify({ name: 'svc', ports: [80, 443] }); // {"name":"svc","ports":[80,443]} ``` With a non-empty `space`, three things change: every property and array element goes on its own line, each nesting level is prefixed by the indent unit repeated once per level, and the separator between a key and its value becomes `": "` rather than `":"`. Empty objects and arrays stay collapsed as `{}` and `[]` — there is nothing inside to put on a line. ```js JSON.stringify({ name: 'svc', ports: [80, 443] }, null, 2); // { // "name": "svc", // "ports": [ // 80, // 443 // ] // } ``` Note also that `space` does not add a trailing newline. If you are writing a config file that a linter expects to end with one, you append it yourself. ## Indentation is not part of the data Whitespace between JSON tokens is insignificant, and `JSON.parse` skips it. Pretty-printed text and compact text describing the same data parse to values that are indistinguishable — the indentation exists purely for a human reader. Only space, tab, carriage return and line feed count as JSON whitespace, which is exactly what `space` produces. ## When to use which Use indentation where a person or a diff will read the text: files checked into a repository (`package.json`-style configuration), snapshot fixtures in tests, a debugging dump, a CLI that prints a result to a terminal. Indented output makes line-based diffs meaningful, which is a real reason to store configuration pretty-printed. Use compact output where a machine will read it: request and response bodies, cache entries, message payloads, high-volume log lines. Indentation is pure overhead there — every level of nesting multiplies the bytes spent on spaces, and for a large deeply-nested document that is a noticeable fraction of the payload. A practical middle ground for logging: keep the stored representation compact and pretty-print only at the moment a human asks to see it, so you are not paying for whitespace on every write. ## The common mistakes The two that come up in interviews are passing the indent as the **second** argument — `JSON.stringify(obj, 2)`, where `2` lands in the replacer slot, is ignored as a replacer, and produces compact output — and assuming a large number gives deeper indentation when the value is clamped to 10.
- What happens if you pass 20 as the space argument?You get 10 spaces per level, not 20. The numeric form of `space` is truncated to an integer and clamped to a maximum of 10, so any larger value behaves exactly like 10. Values of 0 or less mean no indentation at all, which is identical to omitting the argument.
- Someone writes JSON.stringify(obj, 2) expecting indented output and gets a compact string. Why?Because `2` landed in the second parameter, which is the replacer slot, not `space`. A number there is not a valid replacer — it is neither an array of keys nor a function — so it is ignored, and with no third argument there is no indentation. The fix is `JSON.stringify(obj, null, 2)`.
- Does pretty-printing change what JSON.parse produces?No. Whitespace between JSON tokens is insignificant, and the parser skips it, so indented and compact text describing the same data parse to indistinguishable values. Indentation only costs bytes: it is worth paying for files and diffs a human reads, and worth avoiding on wire payloads and high-volume log lines.
saying these in an interview costs you the question
- Passes the indent as the second argument and wonders why nothing indents
- Thinks a larger space number keeps increasing the indent
- Believes indentation changes the parsed value
- Assumes space must be a number and cannot be a string
- Says pretty-printed JSON is a different format from compact JSON