JSON.stringify()

Converts a JavaScript value into a JSON string, with optional filtering and indentation.

Signature
JSON.stringify(value)
JSON.stringify(value, replacer)
JSON.stringify(value, replacer, space)
  • Added in JavaScript ES5

Parameters

ParameterTypeDescription
$valueanyThe value to convert to a JSON string.
$replaceroptionalfunction | Array<string | number> | null= nullA function called as replacer(key, value) for every entry (the root is visited with the key ""), or an array of property names acting as an allowlist at every level. Returning undefined, a function, or a symbol from the function form omits that property.
$spaceoptionalstring | number= undefinedIndentation for readability: a number of spaces (clamped to 10), or a string used as the indent unit (first 10 characters). Anything else produces no whitespace.

Return value

string | undefinedA JSON string representing the value, or undefined when the value itself is undefined, a function, or a symbol.

Errors

Throws a TypeError if the value contains a circular reference, or a BigInt without a custom toJSON() method.

Changelog

  • PHP ES2019Well-formed JSON.stringify: lone surrogates are emitted as \uXXXX escapes instead of invalid UTF-16, so the result is always valid UTF-8 encodable.
  • PHP ES2025Values created by JSON.rawJSON() are spliced into the output as literal JSON text instead of being serialized as objects.

JSON.stringify() walks a value depth-first and produces JSON text. Reach for it when you need to send data over the network, persist it, log a structure in one line, or cheaply compare two plain objects for equality.

What disappears

Only enumerable own string-keyed properties survive. undefined, functions and symbols are dropped from objects but become null inside arrays — so an array keeps its length while an object silently loses keys. NaN and Infinity also become null. A Map, a Set, or a class instance whose data lives in private fields all serialize as {}, which is usually the moment people notice this rule.

Steering the output

A function replacer is called for every key, starting with the root under the key ""; return undefined to strip an entry, or a different value to substitute one. An array replacer is a name allowlist applied at every nesting level, not just the top. space affects whitespace only — the parsed result is identical either way.

Any object can define toJSON(), which runs before the replacer sees the value. That is how a Date arrives as an ISO string, and it is the cleanest hook for serializing your own classes.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Build a request body from a plain object before sending it to an API.
  • Pretty-print a nested config object into a readable, diff-friendly file.
  • Strip internal fields such as passwordHash from a record before logging it.
  • Produce a stable cache key from a small object of query parameters.

Frequently asked questions

Why does JSON.stringify() drop some of my object's properties?

Properties whose value is undefined, a function, or a symbol are omitted from objects, as are all symbol-keyed and non-enumerable properties, and anything inherited from a prototype. Inside an array those same values become null instead, because array positions cannot be skipped.

How do I pretty-print JSON with indentation?

Pass the indent as the third argument: JSON.stringify(value, null, 2) indents with two spaces, and a string such as "\t" is used as the indent unit directly. The second argument must be filled with null when you do not need a replacer.

How do I stringify an object containing a BigInt?

Convert it yourself in a replacer, for example (key, value) => (typeof value === "bigint" ? value.toString() : value), because BigInt has no JSON representation and throws a TypeError by default. If you need the number unquoted in the output, use JSON.rawJSON() instead.

More examples

toJSON() runs before the replacer

The Date and the Money instance are both replaced by their toJSON() results first, so the replacer never sees the original objects — it sees strings. The logged keys show the root being visited as "" before its properties.

Example · javascript
class Money {
  constructor(cents) {
    this.cents = cents;
  }
  toJSON() {
    return (this.cents / 100).toFixed(2);
  }
}

const order = { placed: new Date(Date.UTC(2024, 4, 1)), total: new Money(1999) };
console.log(JSON.stringify(order));
console.log(JSON.stringify(order, (key, value) => {
  console.log("replacer saw", JSON.stringify(key), "->", typeof value);
  return value;
}));

Circular references throw

The first attempt logs TypeError because node references itself. A replacer that tracks already-visited objects in a WeakSet lets the same structure serialize, marking the loop instead of following it forever.

Example · javascript
const node = { name: "root" };
node.self = node;

try {
  JSON.stringify(node);
} catch (err) {
  console.log(err.name);
}

const seen = new WeakSet();
const safe = JSON.stringify(node, (key, value) => {
  if (typeof value === "object" && value !== null) {
    if (seen.has(value)) return "[Circular]";
    seen.add(value);
  }
  return value;
});
console.log(safe);

Indenting with a string, and array holes

space accepts any string as the indent unit, not just spaces or tabs. The second line shows the array rule: every non-serializable element becomes null rather than vanishing. Maps and Sets have no enumerable own properties, so both stringify to {}.

Example · javascript
const data = { levels: [1, [2, [3]]], missing: undefined };

console.log(JSON.stringify(data, null, "··"));
console.log(JSON.stringify([undefined, () => {}, Symbol("x"), NaN]));
console.log(JSON.stringify(new Map([["a", 1]])), JSON.stringify(new Set([1, 2])));

Discussion

  • Be the first to comment on this lesson.