Master JavaScript JSON serialization — what stringify drops, how to handle dates and undefined, the replacer and reviver functions, and circular reference errors.
The Basics
JSON.stringify({ name: "Alice", age: 30 })
// → '{"name":"Alice","age":30}'
JSON.parse('{"name":"Alice","age":30}')
// → { name: "Alice", age: 30 }
What stringify Silently Drops or Converts
JSON.stringify({
fn: () => "hello", // functions → dropped
undef: undefined, // undefined → dropped
sym: Symbol("x"), // symbols → dropped
date: new Date(), // Date → ISO string
nan: NaN, // NaN → null
inf: Infinity, // Infinity → null
regex: /pattern/gi, // RegExp → {}
map: new Map([[1, 2]]), // Map → {}
})
Circular Reference Error
const a = {};
a.self = a;
JSON.stringify(a); // TypeError: Converting circular structure to JSON
// Fix with a WeakSet replacer:
const seen = new WeakSet();
JSON.stringify(a, (key, val) => {
if (typeof val === 'object' && val !== null) {
if (seen.has(val)) return '[Circular]';
seen.add(val);
}
return val;
});
Replacer and Reviver
// Replacer: filter/transform during stringify
JSON.stringify(data, ['name', 'age']) // only include these keys
// Reviver: transform during parse (restore Date objects)
JSON.parse(jsonStr, (key, val) => {
if (typeof val === 'string' && /^d{4}-d{2}-d{2}T/.test(val)) {
return new Date(val);
}
return val;
})
Pretty Printing
JSON.stringify(data, null, 2) // 2-space indent
JSON.stringify(data, null, " ") // tab indent
Deep Clone Patterns
// Quick — loses functions, undefined, Dates become strings
const clone = JSON.parse(JSON.stringify(obj));
// Better: structuredClone() — handles Date, Map, Set, circular refs
const clone = structuredClone(obj); // Node 17+, modern browsers