JSON.stringify turns a JavaScript value into JSON text, ready to save, send, or print. It is the opposite of JSON.parse. Most values convert exactly as you would expect, but some have no JSON form and are quietly dropped, changed, or rejected, so it pays to know the rules.
const user = { name: "Ava", age: 16, tags: ["js", "css"], active: true };
const text = JSON.stringify(user);
console.log(text);
console.log(typeof text);
The text is compact: no extra spaces or line breaks, with every key in double quotes. Anything you can stringify, you can later JSON.parse back into an equal value.
console.log(JSON.stringify("hello"));
console.log(JSON.stringify(42));
console.log(JSON.stringify(true));
console.log(JSON.stringify(null));
console.log(JSON.stringify([1, "two", false]));
The full call is JSON.stringify(value, replacer, space). For now pass null as the replacer and a number as space: that many spaces of indentation are added, and every item goes on its own line.
const user = { name: "Ava", tags: ["js", "css"] };
console.log(JSON.stringify(user, null, 2));
console.log(JSON.stringify(user, null, "--"));
Use the indented form when a person will read it, such as a log or a file you want to open and edit. Use the compact form when size matters, such as data sent over the network. The indentation number is capped at 10.
JSON cannot hold everything JavaScript can. What stringify does about it depends on where the value sits:
| Value | In an object | In an array |
|---|---|---|
undefined, a function, a Symbol | inside an object: the key is left out | inside an array: becomes null |
NaN, Infinity | becomes null | becomes null |
Date | becomes an ISO text string | becomes an ISO text string |
Map, Set | becomes {} | becomes {} |
const thing = { a: 1, b: undefined, c: function () {}, d: Symbol("x"), e: null, f: NaN };
console.log(JSON.stringify(thing));
console.log(JSON.stringify([undefined, function () {}, NaN]));
console.log(JSON.stringify(undefined));
undefined field, or a Map will stringify without complaint and come back from JSON.parse missing pieces.
const scores = new Map([["ava", 92], ["ben", 85]]);
console.log(JSON.stringify(scores));
console.log(JSON.stringify([...scores]));
console.log(JSON.stringify(Object.fromEntries(scores)));
A Date has a toJSON method, and stringify calls it. Any object can define its own toJSON to control how it is written:
console.log(JSON.stringify({ when: new Date(Date.UTC(2026, 9, 2)) }));
const temp = {
celsius: 21,
toJSON() {
return { c: this.celsius, f: this.celsius * 9 / 5 + 32 };
}
};
console.log(JSON.stringify(temp));
The second argument filters or changes the output. Give it an array of key names to keep only those keys:
const user = { name: "Ava", password: "secret", grade: 10, id: 7 };
console.log(JSON.stringify(user, ["name", "grade"]));
Or give it a function, called with each key and value (the same shape as the reviver for parse). Return the value to keep it, a changed value to alter it, or undefined to leave the key out:
const order = { item: "Pen", price: 2.5, secretCode: "x9" };
const text = JSON.stringify(order, (key, value) => {
if (key === "secretCode") return undefined;
if (typeof value === "number") return value * 100;
return value;
});
console.log(text);
Two things make stringify throw instead of dropping a value. A BigInt has no JSON form at all, and an object that contains itself (a circular reference) would need infinite text:
try {
JSON.stringify({ n: 10n });
} catch (error) {
console.log(error.name);
}
const a = { name: "loop" };
a.self = a;
try {
JSON.stringify(a);
} catch (error) {
console.log(error.name);
}
JSON.parse(JSON.stringify(obj)) makes a deep copy of simple data, and you will see it everywhere. It loses dates (they become strings), functions, undefined, and Map/Set, so structuredClone is the better tool when it is available.
What does this log?
console.log(JSON.stringify({ a: 1, b: undefined }));
{"a":1}. A key whose value is undefined is left out of the JSON.What does JSON.stringify([undefined]) give?
"[]""[null]""[undefined]"undefined becomes null so the positions stay in place.How do you make JSON.stringify write each item on its own line, indented by two spaces?
2 as the third argument: JSON.stringify(value, null, 2).Date in it, then parse it back. What is the date now?