JSON is everywhere in modern web development. But working with messy, unformatted JSON is a nightmare. This guide covers everything from basic formatting to advanced techniques with real code examples.
Why JSON Formatting Matters
If you've ever debugged an API response that looks like this:
{"name":"John","age":30,"address":{"street":"123 Main St","city":"NYC"},"hobbies":["reading","coding","hiking"]}
You know the pain. Now compare it with properly formatted JSON:
{
"name": "John",
"age": 30,
"address": {
"street": "123 Main St",
"city": "NYC"
},
"hobbies": [
"reading",
"coding",
"hiking"
]
}
Night and day. Proper formatting makes JSON readable, debuggable, and maintainable.
JavaScript's Built-in JSON Methods
JSON.stringify() with Indentation
The most common way to format JSON in JavaScript:
const data = {
name: "John",
age: 30,
address: {
street: "123 Main St",
city: "NYC"
}
};
// Basic usage (no formatting)
const compact = JSON.stringify(data);
// {"name":"John","age":30,"address":{"street":"123 Main St","city":"NYC"}}
// Pretty print with 2-space indentation
const pretty = JSON.stringify(data, null, 2);
// {
// "name": "John",
// "age": 30,
// "address": {
// "street": "123 Main St",
// "city": "NYC"
// }
// }
The third argument controls indentation:
-
2— 2 spaces (most common) -
4— 4 spaces (classic style) -
"\t"— tab character
JSON.parse() with Validation
Always wrap JSON.parse() in try-catch:
function safeParse(jsonString) {
try {
return JSON.parse(jsonString);
} catch (e) {
console.error('Invalid JSON:', e.message);
return null;
}
}
// Good
safeParse('{"name":"John"}'); // { name: "John" }
// Bad JSON handled gracefully
safeParse('{name: "John"}'); // null (keys must be quoted)
The Replacer Function: Selective Formatting
The second argument of JSON.stringify() is a replacer. It can filter or transform data:
const user = {
name: "John",
password: "secret123",
email: "john@example.com",
token: "abc-xyz-123"
};
// Filter out sensitive fields
const safe = JSON.stringify(user, (key, value) => {
if (['password', 'token'].includes(key)) {
return undefined; // omit from output
}
return value;
}, 2);
// {
// "name": "John",
// "email": "john@example.com"
// }
This is extremely useful when logging API responses — you don't want passwords or tokens in your logs.
Common JSON Formatting Pitfalls
1. Trailing Commas
// ❌ This will throw SyntaxError
JSON.parse('{"a": 1, "b": 2,}');
// ✅ Remove trailing comma
JSON.parse('{"a": 1, "b": 2}');
2. Single Quotes
// ❌ Invalid JSON
JSON.parse("{'name': 'John'}");
// ✅ Use double quotes
JSON.parse('{"name": "John"}');
3. Comments in JSON
// ❌ Standard JSON does not support comments
// JSON.parse('{"a": 1 /* comment */}'); // SyntaxError
// ✅ Use JSON5 if you need comments (requires library)
// Or strip comments before parsing:
function stripComments(str) {
return str.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, '');
}
4. NaN, Infinity, and undefined
const data = { a: NaN, b: Infinity, c: undefined, d: null };
JSON.stringify(data);
// '{"a":null,"b":null,"d":null}'
// NaN and Infinity become null
// undefined is omitted entirely
// null stays as null
This can cause subtle bugs when round-tripping data through JSON.
Working with Large JSON Files
For large JSON payloads, formatting can consume significant memory:
// For large datasets, consider streaming parsers
// Example using the 'stream-json' npm package:
// const pipeline = require('stream.pipeline');
// const streamJson = require('stream-json');
// const {chain} = require('stream-chain');
// For smaller datasets, format on demand:
function formatIfNeeded(jsonString, maxSize = 100000) {
if (jsonString.length < maxSize) {
return JSON.stringify(JSON.parse(jsonString), null, 2);
}
return jsonString; // Skip formatting for very large payloads
}
Validating JSON Structure
A practical validation function:
function validateJSON(jsonString) {
try {
const parsed = JSON.parse(jsonString);
return { valid: true, data: parsed };
} catch (e) {
// Extract position from error message
const posMatch = e.message.match(/position (\d+)/);
const position = posMatch ? parseInt(posMatch[1]) : -1;
return {
valid: false,
error: e.message,
position: position,
context: position > 0
? jsonString.slice(Math.max(0, position - 20), position + 20)
: null
};
}
}
// Usage
const result = validateJSON('{"name": "John",}');
console.log(result);
// {
// valid: false,
// error: "Unexpected token } in JSON at position 18",
// position: 18,
// context: '"John",}'
// }
Quick Formatting Without Code
When you just need to format JSON quickly — debugging an API response, checking a config file, or validating a webhook payload — you don't always want to write code. I keep a JSON Formatter tool bookmarked for exactly this purpose. It handles formatting, validation, minification, and tree view — all in the browser, no data sent to any server.
Summary
| Technique | Use Case |
|---|---|
JSON.stringify(data, null, 2) |
Pretty print in code |
| Replacer function | Filter sensitive data |
JSON.parse() in try-catch |
Safe parsing |
| Validation function | Detailed error reporting |
| Online formatter | Quick debugging |
Key takeaways:
- Always use try-catch with
JSON.parse() - Use the replacer function to strip sensitive data before logging
- Remember that
undefined,NaN, andInfinitydon't survive JSON round-trips - For quick formatting, keep an online tool handy
JSON formatting is a small thing that saves hours of debugging. Get it right early and your future self will thank you.
What's your favorite JSON tip? Share it in the comments below!
Top comments (0)