JSON.stringify() Serialization Rules
JSON.stringify() is the core method in JavaScript for converting objects to JSON strings. Understanding its serialization rules is essential for avoiding unexpected behavior.
const data = {
name: "Alice",
age: 30,
active: true,
score: null,
greet() { return "hi"; },
[Symbol("id")]: 123,
undef: undefined,
};
console.log(JSON.stringify(data, null, 2));
// {
// "name": "Alice",
// "age": 30,
// "active": true,
// "score": null
// }
Key rules:
- Functions and undefined values are silently omitted
- Symbol keys are silently omitted
- null is preserved as JSON
null - NaN and Infinity become
null
replacer Parameter
The replacer parameter can be either a function or an array, providing two completely different filtering mechanisms.
// Function replacer: called for each key-value pair
JSON.stringify(data, (key, value) => {
if (typeof value === "function") return undefined;
return value;
});
// Array replacer: only keep listed keys
JSON.stringify(data, ["name", "age"]);
// '{"name":"Alice","age":30}'
toJSON() Method
If an object defines a toJSON() method, JSON.stringify() will call it and serialize its return value instead.
const user = {
name: "Alice",
password: "secret123",
toJSON() {
return { name: this.name }; // Exclude password
},
};
console.log(JSON.stringify(user)); // '{"name":"Alice"}'
JSON.parse() and reviver
The reviver function allows custom transformation during deserialization.
const jsonStr = '{"name":"Alice","created_at":"2026-07-15T10:30:00Z"}';
const result = JSON.parse(jsonStr, (key, value) => {
if (key.endsWith("_at") && typeof value === "string") {
return new Date(value);
}
return value;
});
console.log(result.created_at); // Date object
Deep Clone Pitfalls
Using JSON.parse(JSON.stringify(obj)) for deep cloning is a common pattern but has significant limitations.
const obj = {
date: new Date(),
regex: /test/gi,
undef: undefined,
nan: NaN,
infinity: Infinity,
map: new Map([["key", "value"]]),
set: new Set([1, 2, 3]),
};
const clone = JSON.parse(JSON.stringify(obj));
// date: string (not Date!)
// regex: {} (empty object!)
// undef: missing entirely
// nan: null
// infinity: null
// map: {} (empty object!)
// set: {} (empty object!)
For reliable deep cloning, use structuredClone() (modern browsers) or libraries like lodash.
Circular Reference Handling
JSON.stringify throws TypeError on circular references:
const obj = {};
obj.self = obj;
// JSON.stringify(obj); // TypeError: Converting circular structure
// Solution: use a replacer with WeakSet
function stringifySafe(obj) {
const seen = new WeakSet();
return JSON.stringify(obj, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) return '[Circular]';
seen.add(value);
}
return value;
});
}
BigInt Handling
BigInt values cannot be directly serialized with JSON.stringify() — it throws a TypeError.
const data = { id: 9007199254740993n };
// JSON.stringify(data); // TypeError: Do not know how to serialize a BigInt
// Solution: use replacer
const json = JSON.stringify(data, (key, value) =>
typeof value === "bigint" ? value.toString() : value
);
// '{"id":"9007199254740993"}'
// Note: After round-trip, id is a string, not BigInt.
// Use a reviver to restore:
const parsed = JSON.parse(json, (key, value) => {
if (key === 'id' && typeof value === 'string') {
return BigInt(value);
}
return value;
});
Third-Party Libraries
For advanced JSON operations, consider these libraries:
- superjson: Supports Date, Map, Set, BigInt, RegExp, and more
- devalue: Fast, safe serialization with support for circular references
- flatted: Handles circular references by tracking object identity
import superjson from "superjson";
const data = {
date: new Date(),
map: new Map([["key", "value"]]),
};
const { json, meta } = superjson.serialize(data);
// json: {"date":"2026-07-15T...","map":[["key","value"]]}
// meta: {"values":{"date":["Date"],"map":["Map"]}}
const restored = superjson.deserialize({ json, meta });
// Perfect round-trip with types preserved
Fetch API Integration
When working with REST APIs, JSON is the standard data format:
// Fetching JSON data
async function getUsers() {
const response = await fetch('/api/users');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json(); // Automatically parses JSON
}
// Sending JSON data
async function createUser(userData) {
const response = await fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(userData),
});
return response.json();
}
LocalStorage Persistence
JSON serialization is essential for browser storage:
// Save to localStorage
function saveState(key, state) {
try {
localStorage.setItem(key, JSON.stringify(state));
} catch (e) {
console.error('Failed to save state:', e);
}
}
// Load from localStorage
function loadState(key, defaultValue = null) {
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : defaultValue;
} catch (e) {
console.error('Failed to load state:', e);
return defaultValue;
}
}
// Usage
const settings = { theme: 'dark', language: 'en' };
saveState('app-settings', settings);
const loaded = loadState('app-settings');
Stream Processing
For large JSON payloads, use ReadableStream:
async function processLargeJSON(url) {
const response = await fetch(url);
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
// Process chunks as needed
}
return JSON.parse(buffer);
}
JSON Schema Validation
Use ajv for runtime JSON Schema validation:
import Ajv from 'ajv';
const ajv = new Ajv();
const schema = {
type: 'object',
properties: {
name: { type: 'string', minLength: 1 },
age: { type: 'integer', minimum: 0 },
email: { type: 'string', format: 'email' }
},
required: ['name', 'age']
};
const validate = ajv.compile(schema);
const data = { name: 'Alice', age: 30 };
if (validate(data)) {
console.log('Valid!');
} else {
console.log('Errors:', validate.errors);
}
Related Resources
- JSON Best Practices — Universal guidelines for all languages
- JSON Schema Validation — Validate your JSON structures
- JSON Security — Protect against common vulnerabilities
- JSON Formatter — Format and validate your JSON online
- JSON Schema Validator — Test your schemas