JSON Formatting Best Practices: How to Write Clean, Readable JSON
JSON (JavaScript Object Notation) is the backbone of modern web development. Every REST API, every config file, every NoSQL database speaks JSON. Yet despite its simplicity, developers consistently make formatting mistakes that break parsers, confuse teammates, and turn debugging sessions into multi-hour ordeals.
This guide covers the JSON syntax rules that trip people up, when to pretty-print versus minify, how to choose naming conventions your whole team can follow, and how JSON stacks up against alternatives like YAML and TOML. Paste your JSON into our JSON Formatter to validate and beautify it instantly.
JSON Syntax Rules (And the Mistakes That Break Everything)
JSON's grammar is tiny — it supports objects, arrays, strings, numbers, booleans, and null. That's it. But the strictness of the grammar is where people stumble. Here are the rules you must follow:
1. No Trailing Commas
This is the single most common JSON error. JavaScript allows trailing commas in objects and arrays, so developers assume JSON does too. It does not.
// INVALID — trailing comma after "admin"
{
"name": "Alice",
"role": "admin",
}
// VALID
{
"name": "Alice",
"role": "admin"
}
Every JSON parser will reject the first example. The error message will often say something cryptic like "Unexpected token } at position 42" — it's almost always a trailing comma.
2. Strings Must Use Double Quotes
JSON requires double quotes for both keys and string values. Single quotes and unquoted keys are invalid.
// INVALID — single quotes
{'name': 'Alice'}
// INVALID — unquoted key
{name: "Alice"}
// VALID
{"name": "Alice"}
This trips up Python developers especially, since Python's json.dumps() correctly outputs double quotes, but printing a Python dict directly uses single quotes. Always use your language's JSON serializer rather than string concatenation.
3. No Comments
JSON has no comment syntax — no //, no /* */, no #. This is a deliberate design choice by Douglas Crockford, who wanted to prevent comments from being used as parsing directives.
// INVALID — comments not allowed
{
// user's display name
"name": "Alice"
}
// Workaround: use a descriptive key or a "_comment" field
{
"_comment": "Configuration for the auth service",
"tokenExpiry": 3600
}
If you need comments in configuration files, consider using JSONC (JSON with Comments, supported by VS Code), JSON5, YAML, or TOML instead.
4. Numbers Cannot Have Leading Zeros or Be NaN/Infinity
// INVALID
{"port": 08080} // leading zero
{"value": NaN} // NaN not allowed
{"value": Infinity} // Infinity not allowed
{"value": +5} // leading plus sign
// VALID
{"port": 8080}
{"value": null} // use null instead of NaN
{"value": 5}
5. No Multi-line Strings
Strings cannot span multiple lines. Use \n for newlines inside a string value.
// INVALID
{"message": "Hello
World"}
// VALID
{"message": "Hello\nWorld"}
Pretty-Print vs. Minified: When to Use Each
JSON can be formatted in two ways: human-readable (pretty-printed) and machine-optimized (minified). Knowing when to use each saves bandwidth and debugging time.
// Pretty-printed (readable)
{
"user": {
"id": 1042,
"name": "Alice Chen",
"roles": ["admin", "editor"]
}
}
// Minified (compact)
{"user":{"id":1042,"name":"Alice Chen","roles":["admin","editor"]}}
| Use Case | Format | Why |
|---|---|---|
| API responses in production | Minified | Reduces payload size by 15-30% |
| Config files (package.json, tsconfig) | Pretty-printed (2 spaces) | Humans read and edit these |
| Log files and debugging | Pretty-printed | Readability is critical for debugging |
| Database storage (MongoDB, etc.) | Minified | Saves disk space |
| Git-tracked files | Pretty-printed | Produces clean, reviewable diffs |
In most languages, switching between formats is a single argument:
// JavaScript
JSON.stringify(data, null, 2) // pretty (2-space indent)
JSON.stringify(data) // minified
# Python
json.dumps(data, indent=2) # pretty
json.dumps(data, separators=(',', ':')) # minified
Nested Object Best Practices
Deep nesting makes JSON hard to read, hard to query, and hard to maintain. A good rule of thumb: if your JSON is nested more than 3-4 levels deep, consider flattening it.
// BAD — 5 levels deep, hard to navigate
{
"company": {
"departments": {
"engineering": {
"teams": {
"frontend": {
"lead": "Alice"
}
}
}
}
}
}
// BETTER — flattened with clear key paths
{
"companyName": "Acme Corp",
"engineeringFrontendLead": "Alice",
"engineeringFrontendSize": 8
}
// BEST — moderate nesting with arrays
{
"company": "Acme Corp",
"teams": [
{
"department": "engineering",
"name": "frontend",
"lead": "Alice",
"size": 8
}
]
}
The "best" version is easy to iterate over, easy to filter, and easy to extend. When designing JSON APIs, think about how the consumer will access the data — arrays of objects are almost always easier to work with than deeply nested trees.
Naming Conventions: camelCase vs. snake_case
There's no official standard for JSON key naming, but consistency within a project is critical. Here's what the major ecosystems use:
| Convention | Example | Used By |
|---|---|---|
| camelCase | firstName |
JavaScript/TypeScript, Java, Google JSON Style Guide |
| snake_case | first_name |
Python, Ruby, PostgreSQL, Twitter API, Stripe API |
| kebab-case | first-name |
Rare in JSON (requires bracket notation in JS) |
| PascalCase | FirstName |
C#/.NET APIs, Azure |
Pick one and enforce it. If your backend is Python and your frontend is JavaScript, decide at the API boundary. Many teams use snake_case in the API response and transform to camelCase on the client side. Libraries like camelcase-keys (npm) and Pydantic (Python) handle this automatically.
Common Validation Errors and How to Fix Them
When your JSON doesn't parse, the error message is often unhelpful. Here's a quick troubleshooting guide for the most frequent failures:
| Error Message | Likely Cause | Fix |
|---|---|---|
| Unexpected token } | Trailing comma before } | Remove the comma before the closing brace |
| Unexpected token ' | Single quotes used instead of double | Replace all ' with " |
| Unexpected token u | Unquoted key or undefined value | Wrap keys in double quotes; use null not undefined |
| Unexpected end of JSON | Missing closing brace or bracket | Count your { } and [ ] — they must match |
| Unexpected token < | Server returned HTML (404/500) instead of JSON | Check the URL and server response status code |
The fastest way to diagnose JSON errors is to paste your data into a JSON validator that highlights the exact line and character position of the error.
JSON vs. YAML vs. TOML: Which Format Should You Use?
JSON isn't the only structured data format. Here's how it compares to the two most common alternatives:
| Feature | JSON | YAML | TOML |
|---|---|---|---|
| Comments | No | Yes (#) | Yes (#) |
| Multi-line strings | No (use \n) | Yes (| and >) | Yes (""") |
| Trailing commas | No | N/A (no commas) | Yes |
| Date type | No (strings only) | Yes | Yes |
| Best for | APIs, data exchange | Config files, CI/CD | App config (Cargo.toml, pyproject.toml) |
| Whitespace-sensitive | No | Yes (indentation is syntax) | No |
| Parser availability | Built into every language | Requires a library | Requires a library |
Rule of thumb: Use JSON for APIs and data interchange. Use YAML for config files that humans edit frequently (Docker Compose, GitHub Actions, Kubernetes). Use TOML for application settings files where you want something cleaner than JSON but less error-prone than YAML.
Working With Large JSON Files
Once your JSON files exceed a few megabytes, standard approaches start to break down. Here are practical strategies:
Use Streaming Parsers
Loading a 500MB JSON file into memory with JSON.parse() will crash your process. Use streaming parsers instead:
// Node.js — stream-json processes items one at a time
const { parser } = require('stream-json');
const { streamArray } = require('stream-json/streamers/StreamArray');
const fs = require('fs');
fs.createReadStream('huge-data.json')
.pipe(parser())
.pipe(streamArray())
.on('data', ({ value }) => {
processItem(value);
});
Use jq for Command-Line Processing
jq is the Swiss Army knife for JSON on the command line. It can filter, transform, and query JSON files of any size:
# Pretty-print a file
cat data.json | jq '.'
# Extract all user names
cat users.json | jq '.[].name'
# Filter users with role "admin"
cat users.json | jq '[.[] | select(.role == "admin")]'
# Count items in an array
cat data.json | jq 'length'
Consider JSON Lines (JSONL) for Large Datasets
Instead of wrapping everything in one giant array, put one JSON object per line. This format is called JSON Lines (or NDJSON) and is much easier to stream, split, and process in parallel:
// Traditional JSON array — must load entirely into memory
[
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"},
{"id": 3, "name": "Charlie"}
]
// JSON Lines (.jsonl) — process line by line
{"id": 1, "name": "Alice"}
{"id": 2, "name": "Bob"}
{"id": 3, "name": "Charlie"}
JSON Lines is used by BigQuery, AWS Athena, and most data pipelines for exactly this reason.
JSON Formatting Checklist
Before committing or sending JSON, run through this quick checklist:
- All keys and string values use double quotes
- No trailing commas after the last item in objects or arrays
- No comments (use JSONC or a separate docs file if needed)
- Consistent naming convention (camelCase or snake_case, not mixed)
- Nesting depth is 3-4 levels or fewer
- Pretty-printed for config files; minified for API responses
- Valid through a JSON linter or formatter tool
- No duplicate keys (parsers handle them inconsistently)
Frequently Asked Questions
Can JSON contain comments?
No. The JSON specification (RFC 8259) does not support comments of any kind. If you need comments in a JSON-like file, use JSONC (supported by VS Code for settings files), JSON5, or switch to YAML or TOML. Some teams use a "_comment" key as a workaround, but this adds data to your payload and is not a universal convention.
Should I use 2-space or 4-space indentation for JSON?
Two spaces is the dominant convention. It's what npm init generates, what the Google JSON Style Guide recommends, and what most formatters default to. Four spaces work fine too, but two spaces keep deeply nested structures more readable without excessive horizontal scrolling. The important thing is consistency — pick one and configure your formatter and editor to enforce it.
Why does JSON not allow trailing commas?
Douglas Crockford intentionally excluded trailing commas to keep the grammar unambiguous and parsing simple. Every token in valid JSON has a clear, deterministic meaning. While JavaScript, Python, and other languages allow trailing commas for convenience, JSON prioritizes interoperability — any conforming parser in any language will produce the same result for the same input.
What's the maximum size of a JSON file?
The JSON spec has no size limit. The practical limit is your parser's memory. Most in-memory parsers (like JSON.parse() in Node.js) struggle with files over 500MB–1GB. For large datasets, use streaming parsers like stream-json (Node), ijson (Python), or JsonReader (Java). Alternatively, switch to JSON Lines format for files that are many gigabytes.
Is JSON faster to parse than YAML or XML?
Generally yes. JSON's simple grammar (6 data types, no references, no anchors) makes it faster to parse than YAML, which has a much more complex specification. XML parsing is also slower due to the verbosity of tags and attributes. For API data exchange, JSON's combination of small size, fast parsing, and universal support makes it the clear winner. Use our JSON Formatter to quickly validate and format any JSON payload.