DevLab
JSON

JSON.stringify and JSON.parse: Edge Cases You Should Know

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

Frequently Asked Questions

Why does JSON.parse(JSON.stringify(obj)) not work as a deep clone?

JSON.stringify silently drops or transforms several JavaScript value types: undefined values are removed entirely, functions are removed, Date objects become ISO strings (and JSON.parse turns them back into strings, not Dates), RegExp objects become empty objects {}, Map and Set become {}, Infinity and NaN become null, and circular references throw an error. If your object contains any of these, the round-trip through JSON will silently corrupt it. Use structuredClone() (available in all modern browsers and Node 17+) for a proper deep clone that handles Dates, Maps, Sets, ArrayBuffers, and circular references.

What is the replacer parameter in JSON.stringify?

The second argument to JSON.stringify can be either an array of property names (acting as a whitelist of which keys to include) or a function that is called for every key-value pair and can transform or filter the output. A replacer function receives (key, value) and returns the value to serialize — returning undefined removes that key from the output. Common uses: redacting sensitive fields like passwords, converting BigInt values to strings (since JSON has no BigInt type), or serializing custom class instances into plain objects.

Does JSON.parse throw on invalid JSON?

Yes. JSON.parse throws a SyntaxError if the input string is not valid JSON. Common causes: trailing commas in arrays or objects (valid in JavaScript but not in JSON), single-quoted strings (JSON requires double quotes), unquoted property names, comments (JSON does not support them), and leading/trailing whitespace around non-string values in some edge cases. Always wrap JSON.parse in a try-catch when parsing user input or data from external APIs. If you want lenient parsing that accepts JavaScript-style syntax, use a library like JSON5.

Practice with these tools

More Learning Topics

RegexRegex Basics: A Complete Beginner's GuideRegexRegex Special Characters: Complete ReferenceRegexRegex Groups and Captures ExplainedRegexRegex Quantifiers: Complete GuideCSSCSS Selectors: The Complete GuideCSSCSS Specificity: Why Your Styles Aren't ApplyingJSONJSONPath Syntax: Query JSON Like XPathTimeUnix Timestamps ExplainedEncodingBase64 Encoding ExplainedEncodingJWT Structure and How It WorksEncodingJWT vs Session Tokens: Which Should You Use?EncodingJWT Refresh Tokens ExplainedCryptoHash Functions Explained: MD5, SHA-256, and When to Use EachEncodingURL Encoding Explained: What %20 Actually MeansJSONJSON Schema Explained: Validate Your JSON DataJSONJSON vs YAML: Which Should You Use?RegexRegex Lookahead and Lookbehind: Match Without ConsumingRegexRegex for Email Validation: The Right ApproachCSSThe CSS Box Model: margin, padding, border, and contentCSSFlexbox vs CSS Grid: When to Use EachCSSCSS Custom Properties (Variables) ExplainedTimeISO 8601 Explained: The Right Way to Format DatesTimeUnix Timestamps vs ISO 8601: Which to Use in Your API?EncodingUTF-8 Explained: How Computers Store TextTextCORS Explained: Why Your API Call is BlockedTextHTTP Status Codes: A Practical Developer GuideRegexNamed Capture Groups in Regex: Clean ExtractionsColorsColor Spaces Explained: RGB, HSL, HEX, and BeyondColorsColor Contrast for Developers: WCAG Rules and How to CheckNumbersNumber Bases Explained: Binary, Octal, Decimal, and HexNumbersBitwise Operations for Web DevelopersHTMLHTML Semantic Elements: A Complete ReferenceTextText Encoding for Developers: ASCII, UTF-8, and UnicodeRegexWhat is Regex? Complete Guide for DevelopersJSONJSON Format Explained: Structure, Syntax, and Common ErrorsEncodingHow JWT Works: Header, Payload, Signature DecodedHTMLHow to Convert Any Website to Markdown (for LLMs, RAG & Docs)TextPreparing Website Content for RAG: Clean Markdown Pipelines