← Blog ·

Working with JSON in JavaScript: Parse, Stringify, and Common Pitfalls

The two functions you need

JavaScript's entire JSON API is two methods on the global JSON object:

  • JSON.parse(text) — turns a JSON string into a JS value
  • JSON.stringify(value) — turns a JS value into a JSON string

Everything else is a variation on these.

Pretty-printing with stringify

The third argument controls indentation:

JSON.stringify(obj, null, 2)   // two-space indent
JSON.stringify(obj, null, '\t') // tab indent

The second argument is a replacer that lets you filter or transform values on the way out:

// Drop the password before logging
JSON.stringify(user, (key, value) => key === 'password' ? undefined : value)

Transforming values on parse with a reviver

Want to turn ISO date strings back into Date objects?

JSON.parse(text, (key, value) => {
  if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
    return new Date(value)
  }
  return value
})

Things that don't survive a JSON round-trip

JSON only knows about strings, numbers, booleans, null, arrays and plain objects. These all get mangled or dropped:

  • undefined → omitted from objects, becomes null in arrays
  • Date → ISO string (one-way; parse won't restore it)
  • Map, Set{}
  • Functions → omitted
  • BigInt → throws a TypeError
  • Circular references → throws a TypeError

Handling BigInt

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

Workaround:

JSON.stringify(obj, (k, v) => typeof v === 'bigint' ? v.toString() : v)

Detecting circular references

function safeStringify(obj) {
  const seen = new WeakSet()
  return JSON.stringify(obj, (k, v) => {
    if (typeof v === 'object' && v !== null) {
      if (seen.has(v)) return '[Circular]'
      seen.add(v)
    }
    return v
  })
}

The number precision trap

JSON has one number type — IEEE-754 doubles. Integers larger than Number.MAX_SAFE_INTEGER (2^53 - 1) lose precision:

JSON.parse('{"id": 9007199254740993}').id  // 9007199254740992 — wrong!

Fix: serialize big integers as strings on the server, parse them as BigInt on the client.

Common error: "Unexpected token"

This means JSON.parse was given something that isn't valid JSON. Most often:

  • The server returned HTML instead of JSON (check the response status)
  • The JSON has a trailing comma or unquoted key
  • There's a BOM at the start of the file

Validate your JSON here — it'll point to the exact line and column.

Performance tips

  • For large payloads, prefer fetch's response.json() over JSON.parse(await response.text()) — engines can stream-parse.
  • Don't JSON.parse(JSON.stringify(obj)) to deep clone. Use structuredClone(obj) instead — it's faster and handles more types.
  • Profile before optimizing. JSON.parse is highly tuned and rarely the bottleneck.

Try the tools: JSON Formatter · Validator · Minifier