Pretty-print JSON in JavaScript

JSON.stringify(JSON.parse(text), null, 2)

JSON.stringify(value, null, 2) writes a value as JSON with each member on its own line, indented two spaces per level. When what you have is a JSON string, parse it first: JSON.stringify(JSON.parse(text), null, 2). The second argument is the replacer (null for none) and the third is the indent.

const text = '{"name":"café","id":42,"tags":["api","ops"],"active":true,"manager":null}';
console.log(JSON.stringify(JSON.parse(text), null, 2));
{
  "name": "café",
  "id": 42,
  "tags": [
    "api",
    "ops"
  ],
  "active": true,
  "manager": null
}

The formatter below prints the same for that text with 2 spaces. Skip the JSON.parse and you get the string back as one quoted JSON string: "{\"name\":\"café\",\"id\":42,...}".

The indent and the replacer

The indent is a number of spaces, capped at 10, or a string such as '\t', cut to its first 10 characters. The replacer is either an array of the keys to keep or a function that sees every key and value and returns what to write; returning undefined drops the property.

const user = { id: 42, name: 'Ada', password: 'hunter2' };
JSON.stringify(user, ['id', 'name']);                                       // '{"id":42,"name":"Ada"}'
JSON.stringify(user, (key, value) => (key === 'password' ? undefined : value)); // '{"id":42,"name":"Ada"}'

What a round trip changes

JSON.parse reads every number as an IEEE 754 double, which is exact only up to 2^53 − 1 (Number.MAX_SAFE_INTEGER, 9007199254740991). Past that, digits change:

JSON.stringify(JSON.parse('{"price":1.10,"id":12345678901234567890,"exp":1e5,"big":1e1000}'));
// '{"price":1.1,"id":12345678901234567000,"exp":100000,"big":null}'

1e1000 overflows to Infinity, and JSON.stringify writes Infinity and NaN as null. undefined, functions and symbols disappear from objects and become null in arrays, and a Date becomes its ISO string. The formatter below works on the text’s own tokens, so 1.10 and all 20 digits of that id survive there.

BigInt

JSON.stringify({ id: 1n }) throws TypeError: Do not know how to serialize a BigInt. Convert it in the replacer, to a string or, in Node 22, to a bare number with JSON.rawJSON:

const data = { id: 12345678901234567890n };
JSON.stringify(data, (k, v) => (typeof v === 'bigint' ? v.toString() : v));             // '{"id":"12345678901234567890"}'
JSON.stringify(data, (k, v) => (typeof v === 'bigint' ? JSON.rawJSON(v.toString()) : v)); // '{"id":12345678901234567890}'

Node 22 also hands a reviver the number’s source text, so big integers can be read without loss: JSON.parse(text, (k, v, { source }) => (k === 'id' ? BigInt(source) : v)). Check your browser targets before relying on either.

From the shell

node -e 'console.log(JSON.stringify(JSON.parse(require("fs").readFileSync(0, "utf8")), null, 2))' < data.json

Try it in the JSON Formatter & Validator

Result
{
  "name": "café",
  "id": 42,
  "tags": [
    "api",
    "ops"
  ],
  "active": true,
  "manager": null
}

5 keys, nesting 2, 74 bytes in → 108 bytes out

Runs in your browser. Nothing you enter is sent anywhere.