Working with JSON (JavaScript Object Notation) is a fundamental skill for any JavaScript developer. Whether you're fetching data from an API, storing state in localStorage, or communicating between services, two methods play a central role:
- JSON.parse() – converts JSON strings into JavaScript objects.
- JSON.stringify() – converts JavaScript objects into JSON strings.
In this article, we’ll cover how they work, their options, pitfalls, and advanced use cases.
What is JSON?
JSON (JavaScript Object Notation) is a lightweight data format used to represent structured data. It’s human-readable and language-independent, making it the most common format for APIs and data storage.
A JSON string looks like this:
{
"name": "Alice",
"age": 25,
"isAdmin": false,
"skills": ["JavaScript", "React", "Node.js"]
}
But in JavaScript, this would be represented as an object:
{
name: "Alice",
age: 25,
isAdmin: false,
skills: ["JavaScript", "React", "Node.js"]
}
JSON.parse()
Basic Usage
JSON.parse() takes a JSON string and converts it into a JavaScript object:
const jsonString = '{"name":"Alice","age":25}';
const obj = JSON.parse(jsonString);
console.log(obj.name); // "Alice"
console.log(obj.age); // 25
With Arrays
const jsonArray = '[1, 2, 3, 4]';
const arr = JSON.parse(jsonArray);
console.log(arr[0]); // 1
Error Handling
If the string is not valid JSON, it throws a SyntaxError:
try {
JSON.parse("{name: 'Alice'}"); // Invalid JSON
} catch (e) {
console.error("Parsing failed:", e.message);
}
Tip: Always wrap JSON.parse() in try...catch when working with external data.
The reviver Function
JSON.parse() can take a second argument — a reviver function — to transform values while parsing:
const data = '{"name":"Alice","birthYear":1995}';
const obj = JSON.parse(data, (key, value) => {
if (key === "birthYear") {
return new Date().getFullYear() - value; // Convert to age
}
return value;
});
console.log(obj); // { name: "Alice", birthYear: 30 } (assuming current year = 2025)
JSON.stringify()
Basic Usage
**JSON.stringify() **converts an object into a JSON string:
const obj = { name: "Alice", age: 25 };
const jsonString = JSON.stringify(obj);
console.log(jsonString);
// '{"name":"Alice","age":25}'
With Arrays
const arr = [1, 2, 3];
console.log(JSON.stringify(arr)); // "[1,2,3]"
Handling Non-Serializable Values
- undefined, functions, and symbols are skipped:
const obj = { a: 1, b: undefined, c: () => {} };
console.log(JSON.stringify(obj)); // '{"a":1}'
- NaN, Infinity, and -Infinity become null:
console.log(JSON.stringify([NaN, Infinity, -Infinity])); // "[null,null,null]"
The replacer Function
You can control what gets included using a replacer function or array:
const user = { name: "Alice", age: 25, password: "secret" };
// Function replacer
console.log(JSON.stringify(user, (key, value) => {
if (key === "password") return undefined;
return value;
}));
// '{"name":"Alice","age":25}'
// Array replacer (whitelist keys)
console.log(JSON.stringify(user, ["name", "age"]));
// '{"name":"Alice","age":25}'
Pretty Printing (Indentation)
You can add spacing to make JSON more readable:
const obj = { name: "Alice", age: 25 };
console.log(JSON.stringify(obj, null, 2));
Output:
{
"name": "Alice",
"age": 25
}
Common Use Cases
Local Storage
localStorage only stores strings, so JSON.stringify() and JSON.parse() are essential:
const user = { name: "Alice", age: 25 };
// Save
localStorage.setItem("user", JSON.stringify(user));
// Retrieve
const savedUser = JSON.parse(localStorage.getItem("user"));
console.log(savedUser.name); // "Alice"
API Requests & Responses
Most APIs send and receive JSON:
fetch("/api/user")
.then(res => res.json()) // equivalent to JSON.parse()
.then(data => console.log(data));
When sending data:
fetch("/api/user", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "Alice", age: 25 })
});
Deep Cloning Objects
A common trick is to deep-clone an object with JSON:
const obj = { name: "Alice", skills: ["JS"] };
const clone = JSON.parse(JSON.stringify(obj));
clone.skills.push("React");
console.log(obj.skills); // ["JS"]
console.log(clone.skills); // ["JS", "React"]
⚠️ Caveat: This fails with functions, Dates, Map, Set, and other non-JSON values.
Pitfalls & Limitations
Circular References
Objects with self-references cannot be stringified:
const obj = {};
obj.self = obj;
JSON.stringify(obj); // Throws TypeError
Loss of Data Types
- Dates become strings
- undefined, functions, symbols are removed
- Map, Set, RegExp are not preserved
Performance Concerns
For very large objects, JSON.stringify() and JSON.parse() can be slow. Libraries like flatted or superjson help handle advanced cases.
. Alternatives & Advanced Tools
structuredClone() – modern alternative for deep cloning, supports more types than JSON.parse(JSON.stringify(...)).
Libraries:
superjson
for preserving Dates, Maps, Sets.flatted
for circular structures.
Conclusion
JSON.parse() and JSON.stringify() are deceptively simple but incredibly powerful. They’re essential for:
- Converting between strings and objects
- Safely storing and transmitting data
- Controlling serialization with replacer/reviver
- Debugging with pretty-printing
By mastering these two methods, you’ll unlock smoother workflows with APIs, storage, and data manipulation in JavaScript.
Top comments (0)