← All articles

How to Stringify JSON with JSON.stringify

JSON.stringify converts a value to a JSON string. Learn the space and replacer arguments, the toJSON hook, and the values it silently drops or throws on.

JSON.stringify() converts a JavaScript value into a JSON string — for sending in a request body, saving to a file, or storing in localStorage. It looks simple, but its three arguments, its handling of special values, and the toJSON hook trip up developers constantly. This guide covers everything JSON.stringify() does, the equivalents in other languages, and the pitfalls to avoid.

What JSON.stringify Does

It walks a value recursively and produces a spec-compliant JSON string. By default the output is compact, with no extra whitespace:

const user = { name: "Ada", age: 36, active: true };

JSON.stringify(user);
// '{"name":"Ada","age":36,"active":true}'

Because it always emits valid JSON, you should never build JSON strings by hand with concatenation or template literals — doing so is the number-one cause of [object Object] errors and trailing-comma errors.

The Three Arguments

JSON.stringify(value, replacer, space) takes a value plus two optional arguments that most developers never use — but they're powerful.

space — pretty-printing

The third argument controls indentation. Pass a number for that many spaces, or a string like '\t' for tabs:

JSON.stringify(user, null, 2);
// {
//   "name": "Ada",
//   "age": 36,
//   "active": true
// }

This is exactly how you pretty-print JSON in code — see How to Format JSON. Omit the argument (or pass 0) to minify; see How to Minify JSON.

replacer — filtering and transforming

The second argument can be an array of keys to include, or a function that transforms each value:

// Allow-list: only these keys survive
JSON.stringify(user, ['name', 'active']);
// '{"name":"Ada","active":true}'

// Function: redact a field, drop another
JSON.stringify(
  { name: "Ada", password: "secret", token: "abc" },
  (key, value) => {
    if (key === 'password') return '[REDACTED]';
    if (key === 'token') return undefined; // returning undefined omits the key
    return value;
  },
);
// '{"name":"Ada","password":"[REDACTED]"}'

The toJSON Hook

If a value has a toJSON() method, JSON.stringify() calls it and serialises the return value instead. This is how Date objects become ISO strings automatically:

JSON.stringify({ when: new Date('2026-05-24T00:00:00Z') });
// '{"when":"2026-05-24T00:00:00.000Z"}'  ← Date.prototype.toJSON ran

// Add your own toJSON for custom serialisation
class Money {
  constructor(cents) { this.cents = cents; }
  toJSON() { return (this.cents / 100).toFixed(2); }
}
JSON.stringify({ price: new Money(1999) });
// '{"price":"19.99"}'

What Gets Dropped or Converted

JSON has only six types, so JavaScript values without a JSON equivalent are silently dropped or coerced — a frequent source of round-trip bugs:

JavaScript valueResult of JSON.stringify
undefined (in object)key omitted
undefined (in array)null
functionomitted (object) / null (array)
Symbolomitted
NaN, Infinitynull
DateISO string (via toJSON)
BigIntthrows TypeError

For the full list of how JSON differs from JavaScript objects, see JSON vs JavaScript objects.

Common Pitfalls

Circular references throw

const a = {};
a.self = a;
JSON.stringify(a);
// TypeError: Converting circular structure to JSON

// Fix: a replacer that tracks seen objects, or restructure the data

BigInt isn't supported

JSON.stringify({ id: 9007199254740993n });
// TypeError: Do not know how to serialize a BigInt

// Fix: convert to string first, or add a replacer
JSON.stringify({ id: 9007199254740993n }, (k, v) =>
  typeof v === 'bigint' ? v.toString() : v
);

Large integers lose precision on round-trip

Numbers above 253 can't be represented exactly. If you stringify and later parse a large integer ID, it may change. Store such IDs as strings.

Adjacent APIs: structuredClone, the reviver, and Stable Stringify

  • structuredClone — for in-memory deep copies, don't use JSON.parse(JSON.stringify(x)). structuredClone handles Date, Map, Set, ArrayBuffer, and circular references natively, and is far faster.
  • The reviver argument to JSON.parse mirrors the replacer here. Pair them when you need lossless round-trips for types that JSON doesn't model — e.g. encode Date as an ISO string in replacer and rehydrate it in reviver.
  • Stable-stringify libraries (e.g. json-stable-stringify, fast-json-stable-stringify) — produce the same string regardless of key insertion order. Use them when the output is hashed, signed, or compared as text (see RFC 8785 / JCS for the formal version).

Stringify in Other Languages

# Python
import json
json.dumps({"name": "Ada"})                       # compact
json.dumps({"name": "Ada"}, indent=2)             # pretty
json.dumps({"name": "Ada"}, separators=(',', ':')) # minified

// Go
import "encoding/json"
b, _ := json.Marshal(map[string]string{"name": "Ada"})

// Ruby
require 'json'
{ name: "Ada" }.to_json

Stringify JSON in Your Browser

Need to escape a JSON value into a string literal — for embedding in code, a database column, or a shell command? fixjson's JSON Stringify tool does it instantly, in your browser, with no data sent to any server.

Frequently Asked Questions

How do I pretty-print with JSON.stringify?

Pass a third argument: JSON.stringify(value, null, 2) for 2-space indentation, or '\t' for tabs. See How to Format JSON.

Why does JSON.stringify drop some of my properties?

Properties with undefined, function, or Symbol values are omitted because JSON has no representation for them. Use a replacer or convert them first.

How do I stringify a value that contains BigInt?

JSON.stringify() throws on BigInt. Provide a replacer that calls .toString() on bigint values, and parse them back deliberately on the other side.

What's the difference between JSON.stringify and JSON.parse?

stringify turns a value into a JSON string; parse turns a JSON string back into a value. They're inverses. For parsing pitfalls see Handling broken JSON in JavaScript.

How do I JSON.stringify a Map or Set?

Out of the box, both serialise to "{}" — a Map has no enumerable own properties and a Set stringifies as an empty object. Convert them to a JSON-friendly shape first (or via a replacer):

// Map → array of [key, value] pairs (round-trippable)
JSON.stringify([...myMap]);
// or with a replacer:
JSON.stringify({ data: myMap }, (k, v) =>
  v instanceof Map ? Object.fromEntries(v) :
  v instanceof Set ? [...v] : v
);

On the way back, rebuild with new Map(arr) / new Set(arr) in a JSON.parse reviver, since plain JSON has no Map/Settype.

Try It Now