JSON.stringify()
JSON.stringify(value, replacer, space) JSON.stringify() converts a JavaScript value into a JSON string. This is the standard way to serialize data for APIs, localStorage, or network transmission. The method handles objects, arrays, primitives, and special values like null and undefined.
Syntax
JSON.stringify(value)
JSON.stringify(value, replacer)
JSON.stringify(value, replacer, space)
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
value | any | — | The value to convert to a JSON string. Can be any valid JavaScript value including objects, arrays, strings, numbers, booleans, and null. |
replacer | function | array | null | null | A function that transforms the stringification process, or an array of strings and numbers specifying which properties to include. If null, all properties are included. |
space | number | string | null | null | A number (1-10) of spaces for indentation, or a string to use as the indent. Makes the output readable. |
The replacer Parameter
When replacer is a function, it receives two arguments: the key and the value being stringified. The function runs for the initial object too (with an empty string as key). Return values behave like this:
- Return a number, string, boolean, or null — that value is used directly
- Return undefined — the property is omitted from the output
- Return any other object — it gets recursively stringified
When replacer is an array, only the properties whose names appear in the array are included. Symbol keys are always ignored.
The space Parameter
Pass a number to use that many spaces for indentation (clamped to a maximum of 10). Pass a string to use that string as the indent — only the first 10 characters are used. This makes debugging easier by producing human-readable output.
Examples
Basic Stringification
// Stringify an object
const user = { name: "Alice", age: 30 };
console.log(JSON.stringify(user));
// {"name":"Alice","age":30}
// Stringify an array
const nums = [1, 2, 3];
console.log(JSON.stringify(nums));
// [1,2,3]
// Stringify a primitive
console.log(JSON.stringify("hello"));
// "hello"
console.log(JSON.stringify(true));
// true
Basic stringification compresses objects and arrays into compact JSON with no whitespace. For more control — like stripping sensitive fields or transforming values during serialization — you can pass a replacer function that receives each key-value pair and decides what to emit.
Using the replacer Function
const user = {
name: "Alice",
age: 30,
password: "secret123",
email: "alice@example.com"
};
// Remove sensitive fields
const safeJson = JSON.stringify(user, (key, value) => {
if (key === "password") return undefined;
return value;
});
console.log(safeJson);
// {"name":"Alice","age":30,"email":"alice@example.com"}
// Transform values
const data = { price: 19.99, tax: 1.50 };
const formatted = JSON.stringify(data, (key, value) => {
if (typeof value === "number") return value.toFixed(2);
return value;
});
console.log(formatted);
// {"price":"19.99","tax":"1.50"}
The replacer function gives you fine-grained control over which properties survive serialization and how their values are formatted. Once you have the right fields selected, the space parameter adds indentation to make the output readable — useful during development, debugging, or when saving formatted JSON fixtures to disk.
Using the space parameter for pretty printing
const data = {
name: "Alice",
skills: ["JavaScript", "Python", "Rust"]
};
// 2-space indentation
console.log(JSON.stringify(data, null, 2));
// {
// "name": "Alice",
// "skills": [
// "JavaScript",
// "Python",
// "Rust"
// ]
// }
// Tab indentation
console.log(JSON.stringify(data, null, "\t"));
// {
// \t"name": "Alice",
// \t"skills": [
// \t\t"JavaScript",
// \t\t"Python",
// \t\t"Rust"
// \t]
// }
Pretty printing makes the JSON output readable, but it does not change what gets included. Some JavaScript values simply do not map to JSON — undefined, NaN, Infinity, and functions are either omitted or converted to null depending on where they appear. Knowing these rules prevents surprise data loss when you deserialize later.
Handling special values
// null becomes "null"
console.log(JSON.stringify(null));
// null
// undefined is omitted
console.log(JSON.stringify({ a: 1, b: undefined, c: null }));
// {"a":1,"c":null}
// NaN and Infinity become null
console.log(JSON.stringify({ a: NaN, b: Infinity }));
// {"a":null,"b":null}
// Functions are omitted
console.log(JSON.stringify({ a: 1, fn: () => {} }));
// {"a":1}
// Arrays with undefined become null
console.log(JSON.stringify([1, undefined, 3]));
// [1,null,3]
The rules for special values are built into JSON.stringify() and you cannot override them. But you can customize serialization entirely by defining a toJSON() method on your class. When JSON.stringify() encounters an object with a toJSON() method, it calls that method and serializes the return value instead of the object itself.
Using toJSON for custom serialization
class User {
constructor(name, password) {
this.name = name;
this.password = password;
}
toJSON() {
return {
name: this.name,
// Don't include password in serialization
};
}
}
const user = new User("Alice", "secret123");
console.log(JSON.stringify(user));
// {"name":"Alice"}
The toJSON() method gives each class control over its own JSON representation. Beyond serialization of individual objects, JSON.stringify() integrates into several common patterns in everyday JavaScript — from persisting data in the browser to sending payloads over the network.
Common Patterns
Storing Data in localStorage
const user = { name: "Alice", theme: "dark" };
// localStorage only stores strings
localStorage.setItem("user", JSON.stringify(user));
// Retrieve and parse
const stored = localStorage.getItem("user");
const parsed = stored ? JSON.parse(stored) : null;
console.log(parsed);
// { name: "Alice", theme: "dark" }
localStorage stores only strings, so every read and write goes through JSON.stringify() and JSON.parse(). The same serialization step is required when sending structured data over HTTP — the Fetch API expects the request body as a string, which JSON.stringify() produces in the exact format that servers expect for application/json content.
Sending Data to an API
const payload = {
userId: 123,
message: "Hello world",
tags: ["greeting", "test"]
};
await fetch("/api/submit", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
});
Sending JSON to an API produces compact output that is efficient over the wire. When you need to inspect that same data during development, adding the space parameter makes nested structures much easier to read. The pretty-printed version is not smaller, but it is vastly more useful when you are scanning logs or stepping through a debugger.
Debugging with pretty print
const complex = {
users: [
{ id: 1, name: "Alice", roles: ["admin", "user"] },
{ id: 2, name: "Bob", roles: ["user"] }
],
metadata: { page: 1, total: 42 }
};
// Easy-to-read output for debugging
console.log(JSON.stringify(complex, null, 2));
Pretty-printing is a diagnostic tool — it helps you see structure at a glance. A different pattern uses JSON.stringify() together with JSON.parse() to create deep copies of plain data objects. This pair of calls produces an independent clone, as long as the original contains only JSON-safe values.
Cloning Objects
const original = { a: 1, b: { c: 2 } };
// Create a deep clone
const clone = JSON.parse(JSON.stringify(original));
clone.b.c = 99;
console.log(original.b.c);
// 2 (original is unchanged)
console.log(clone.b.c);
// 99
Note: This only works for JSON-safe values. Functions, undefined, Symbol keys, and circular references are lost.
How stringify shapes the output
JSON.stringify() does more than convert values into text. It also defines which parts of a JavaScript value are representable in JSON. Objects become key-value text, arrays become ordered lists, and primitive values become their JSON equivalents. Anything outside the JSON data model is either removed, converted, or rejected depending on the case.
That distinction matters when you are building APIs or storage formats. If the data must round-trip later, you should think carefully about which fields survive serialization. The method is good at producing standards-compliant JSON, but it is not a general-purpose object snapshot tool.
Using replacer and space Well
The replacer and space options are what turn JSON.stringify() from a plain serializer into a practical formatting tool. replacer lets you filter sensitive values, rename structures indirectly, or adjust how certain values are encoded. space makes the result easier to inspect during debugging or logging.
The tradeoff is that pretty output is larger. For network payloads, compact output is usually better. For debugging or stored fixtures, indentation is often worth the extra bytes because it makes diffs and manual inspection easier.
JSON-safe data only
The method cannot preserve functions, symbols, circular references, or custom prototypes. That means it is not the right answer for cloning application objects that depend on behavior. Use it when you want wire-format JSON, not when you want a faithful memory copy of a JavaScript value.
See Also
- JSON.parse() — Parse a JSON string into a JavaScript value
- BigInt — Handle integers beyond Number.MAX_SAFE_INTEGER