Working with JSON in JavaScript: The Complete Guide
JSON and JavaScript go together like peanut butter and jelly. The format was literally born from JavaScript object syntax, and JS has built in methods for working with it. Here's everything you need to know.
JSON.parse and JSON.stringify
These are the two core methods. You'll use them constantly.
JSON.parse()
Takes a JSON string, returns a JavaScript object (or array, or primitive).
const jsonString = '{"name": "Alice", "age": 28}';
const user = JSON.parse(jsonString);
console.log(user.name); // "Alice"
console.log(user.age); // 28
JSON.stringify()
Takes a JavaScript value, returns a JSON string.
const user = { name: "Alice", age: 28 };
const jsonString = JSON.stringify(user);
console.log(jsonString); // '{"name":"Alice","age":28}'
Important: stringify doesn't keep everything
Some values get lost or converted:
const obj = {
name: "Alice",
sayHi: function() { return "hi"; }, // Functions: removed
secret: undefined, // undefined: removed
nothing: null, // null: kept
date: new Date(), // Date: becomes string
regex: /test/, // RegExp: becomes {}
infinity: Infinity, // Infinity: becomes null
nan: NaN // NaN: becomes null
};
JSON.stringify(obj);
// '{"name":"Alice","nothing":null,"date":"2026-01-28T...","regex":{},"infinity":null,"nan":null}'
Fetching JSON from APIs
The fetch API is the standard way to get JSON from servers.
Basic GET request
async function getUser(id) {
const response = await fetch(`/api/users/${id}`);
const user = await response.json();
return user;
}
// Usage
const user = await getUser(123);
console.log(user.name);
POST with JSON body
async function createUser(userData) {
const response = await fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(userData),
});
return response.json();
}
// Usage
const newUser = await createUser({ name: "Bob", email: "bob@example.com" });
Handling HTTP errors
response.json() doesn't throw on 404 or 500 errors. You need to check the
status yourself.
async function fetchWithErrorHandling(url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return response.json();
}
Error Handling
JSON.parse throws when given invalid JSON. Always wrap it in try/catch when parsing user input or external data.
Safe parsing
function safeParse(jsonString) {
try {
return JSON.parse(jsonString);
} catch (e) {
console.error('Invalid JSON:', e.message);
return null;
}
}
// Usage
const data = safeParse(userInput);
if (data === null) {
// Handle error
}
With default value
function parseOrDefault(jsonString, defaultValue) {
try {
return JSON.parse(jsonString);
} catch {
return defaultValue;
}
}
// Usage
const config = parseOrDefault(localStorage.getItem('config'), {});
Formatting and Pretty Printing
JSON.stringify takes optional parameters for formatting.
Pretty print with indentation
const user = { name: "Alice", settings: { theme: "dark", notifications: true } };
// Minified (default)
JSON.stringify(user);
// '{"name":"Alice","settings":{"theme":"dark","notifications":true}}'
// Pretty with 2 space indent
JSON.stringify(user, null, 2);
// {
// "name": "Alice",
// "settings": {
// "theme": "dark",
// "notifications": true
// }
// }
// With tabs
JSON.stringify(user, null, '\t');
The replacer parameter
The second parameter lets you filter or transform values.
const user = { name: "Alice", password: "secret123", age: 28 };
// Array replacer: only include specified keys
JSON.stringify(user, ['name', 'age']);
// '{"name":"Alice","age":28}'
// Function replacer: transform values
JSON.stringify(user, (key, value) => {
if (key === 'password') return undefined; // Remove
return value;
});
// '{"name":"Alice","age":28}'
Working with Dates
JSON has no date type. Dates become ISO strings when stringified, and stay strings when parsed. You have to convert them back yourself.
The problem
const event = { name: "Meeting", date: new Date('2026-03-15') };
const json = JSON.stringify(event);
// '{"name":"Meeting","date":"2026-03-15T00:00:00.000Z"}'
const parsed = JSON.parse(json);
console.log(parsed.date instanceof Date); // false
console.log(typeof parsed.date); // "string"
Using a reviver function
function dateReviver(key, value) {
// Check if value looks like an ISO date string
if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
return new Date(value);
}
return value;
}
const parsed = JSON.parse(json, dateReviver);
console.log(parsed.date instanceof Date); // true
JSON and TypeScript
TypeScript doesn't automatically type your parsed JSON. You need to tell it.
Type assertion
interface User {
name: string;
age: number;
}
const user = JSON.parse(jsonString) as User;
With validation (recommended)
Type assertions don't actually validate the data. For runtime safety, use a library like Zod:
import { z } from 'zod';
const UserSchema = z.object({
name: z.string(),
age: z.number(),
});
type User = z.infer<typeof UserSchema>;
function parseUser(json: string): User {
const data = JSON.parse(json);
return UserSchema.parse(data); // Throws if invalid
}
Common Patterns
Deep clone an object
Quick way to clone an object. Has limitations (loses functions, dates become strings).
const clone = JSON.parse(JSON.stringify(original));
For proper cloning, use structuredClone() in modern browsers.
Local storage
// Save
const settings = { theme: 'dark', fontSize: 14 };
localStorage.setItem('settings', JSON.stringify(settings));
// Load
const settings = JSON.parse(localStorage.getItem('settings')) || {};
Query strings
// Encode object as query param
const filters = { status: 'active', limit: 10 };
const url = `/api/users?filters=${encodeURIComponent(JSON.stringify(filters))}`;
Reading JSON files in Node.js
// ES modules
import data from './data.json' assert { type: 'json' };
// CommonJS
const data = require('./data.json');
// Or read manually
const fs = require('fs');
const data = JSON.parse(fs.readFileSync('./data.json', 'utf8'));
Performance Optimization
When building enterprise AI systems for translation and data processing, I've learned that JSON parsing can become a bottleneck with large datasets. Here's what I've found works in production.
JSON.parse performance tips
In my experience, JSON.parse is fast but it blocks the main thread. For files under 1MB, you won't notice. Above that, you need strategies.
// Measure parse time for optimization
console.time('parse');
const data = JSON.parse(largeJsonString);
console.timeEnd('parse');
// For truly massive JSON, consider streaming approaches
// or breaking into smaller chunks before parsing
Streaming large JSON with Worker threads
When I built AI translation systems that process multi-megabyte JSON files, blocking the UI for 2-3 seconds wasn't acceptable. Web Workers solve this.
// main.js
const worker = new Worker('parser-worker.js');
worker.postMessage({ json: largeJsonString });
worker.onmessage = (e) => {
const data = e.data;
console.log('Parsed in worker:', data);
};
// parser-worker.js
self.onmessage = (e) => {
const parsed = JSON.parse(e.data.json);
self.postMessage(parsed);
};
When to avoid JSON
For extremely large datasets (50MB+), I've found that streaming parsers or binary formats like MessagePack can be 5-10x faster. The cost is complexity and tooling.
Common Gotchas
I've debugged enough JSON issues to know these patterns by heart. Here are the traps I see developers fall into repeatedly.
Date handling
JSON has no date type. Dates become ISO strings, and you must convert them back manually. I spent 2 hours once debugging why date comparisons failed after serialization.
const event = { date: new Date('2026-03-15') };
const json = JSON.stringify(event);
// '{"date":"2026-03-15T00:00:00.000Z"}'
const parsed = JSON.parse(json);
console.log(parsed.date instanceof Date); // false - it's a string!
// Solution: use a reviver or convert manually
const revived = JSON.parse(json, (key, value) => {
if (key === 'date') return new Date(value);
return value;
});
Precision loss with large numbers
JavaScript numbers are IEEE 754 floats with 53 bits of precision. When translating product catalogs with 19-digit IDs from e-commerce systems, I learned this the hard way.
const bigId = 9007199254740993;
JSON.parse(JSON.stringify({ id: bigId }));
// { id: 9007199254740992 } - precision lost!
// Solution: use strings for large integers
const safe = { id: "9007199254740993" };
Circular references crash JSON.stringify
Objects referencing themselves will throw an error. In my experience building data processing pipelines, this happens most often with DOM nodes or complex object graphs.
const obj = { name: 'Alice' };
obj.self = obj;
JSON.stringify(obj);
// TypeError: Converting circular structure to JSON
// Solution: use a seen set to detect cycles
function safeStringify(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;
});
}