JSON vs. JSON5 vs. JSONC: Which One and When
Three formats that look nearly identical, behave completely differently, and produce a steady stream of parser errors when confused. A practical guide to telling them apart.
Spend enough time writing configuration files and you notice something: what people call “JSON” isn’t one format. It’s three, and they’re not interchangeable. A config that loads cleanly in VS Code might throw a syntax error when you feed it to JSON.parse. A Webpack config that works in one repo crashes in another. All the files end in .json, they all look like JSON, but the parser underneath is doing different work.
This is a quick tour of what each format actually allows, where each one shows up in the wild, and how to stop guessing which parser you’re talking to.
Plain JSON, to the spec
JSON as defined by RFC 8259 is a stripped-down subset of JavaScript object literals. The grammar fits on a napkin:
- Objects:
{}with string keys and any JSON value - Arrays:
[]with any JSON values - Strings: double-quoted, with a specific escape set
- Numbers: decimal, optionally signed, with
.for decimals andefor exponent true,false,null
That’s the whole language. A few consequences of “that’s the whole language” catch people:
- No comments. Not
//, not/* */. Douglas Crockford removed them deliberately because people were using them as pragma syntax (// @charset: utf-8) and that was turning every parser into a preprocessor. - No trailing commas.
[1, 2, 3,]is invalid. This one burns people constantly when they’re editing a list and forget to delete the last comma. - Keys must be quoted strings.
{foo: 1}is JavaScript, not JSON.{"foo": 1}is JSON. - No hex, no
NaN, noInfinity, noundefined. Number literals must fit the spec’s grammar.{"ratio": NaN}is not a legal JSON document even though JavaScript will happily produce it withJSON.stringifyin some implementations. - Duplicate keys are implementation-defined. Most parsers take the last occurrence. A few error out. The spec says “unpredictable.”
The strictness is the point. When every parser agrees on the grammar, files round-trip reliably across languages. You can write JSON in Python, read it in Go, and the bytes mean the same thing.
Our JSON Formatter runs against the strict grammar — if it reports an error on a file that looks fine in your editor, the file is probably JSON5 or JSONC, not JSON.
JSONC: JSON with comments
JSONC is what Microsoft uses for VS Code configuration files — settings.json, tsconfig.json, launch.json, everything in the .vscode/ directory. It’s plain JSON plus two things:
//single-line comments/* */block comments
That’s it. Everything else is strict JSON. No trailing commas, no unquoted keys, no hex.
JSONC exists because configuration files benefit from being self-documenting, and losing your explanation of why a flag is set when you strip comments is a real cost. The VS Code team shipped their own parser (jsonc-parser, npm package) that handles it.
A tricky gotcha: tsconfig.json is JSONC. If you try to parse it with vanilla JSON.parse after a user has added a helpful // disable strict mode for now comment, you’ll get a parse error. Use jsonc-parser or strip comments first.
JSON5
JSON5 is a different beast. Where JSONC adds two features on top of JSON, JSON5 extends the grammar substantially:
//and/* */comments- Trailing commas in objects and arrays
- Unquoted keys (must be valid JavaScript identifiers)
- Single-quoted strings
- Multi-line strings via line continuation (
\<newline>) - Hexadecimal numbers (
0xdeadbeef) - Leading or trailing decimal points (
.5,5.) +Infinity,-Infinity,NaNas valid number literals- Explicit plus sign on numbers (
+42)
The JSON5 goal is to be a superset of JSON that’s also a strict subset of ES5 — which means valid JSON5 is valid JavaScript you could paste into a .js file. The appeal is ergonomics: hand-written config files benefit from every one of those affordances.
Where you’ll hit JSON5 in practice: Babel’s .babelrc.json5 (when configured that way), some ESLint configurations, and several build tools that let you pick your config syntax. You need a specific parser like the json5 npm package; JSON.parse will reject it.
A quick reference
| Feature | JSON | JSONC | JSON5 |
|---|---|---|---|
Comments (//, /* */) | ❌ | ✅ | ✅ |
| Trailing commas | ❌ | ❌ | ✅ |
| Unquoted keys | ❌ | ❌ | ✅ |
| Single-quoted strings | ❌ | ❌ | ✅ |
| Hex numbers | ❌ | ❌ | ✅ |
NaN / Infinity | ❌ | ❌ | ✅ |
Parsed by JSON.parse | ✅ | ❌ (comments reject) | ❌ |
| File extension | .json | .json, .jsonc | .json5, .json |
Which one to use
The decision hinges on who’s reading the file.
Use plain JSON when the file is machine-generated or machine-read. API responses, data exports, logs, analytics payloads, schema files that tools consume. Every language in existence has a fast, strict JSON parser. Keeping to the spec means no one has to think about which dialect your API is using.
Use JSONC when the file is hand-edited config for a tool that natively supports it. VS Code settings, tsconfig.json, anything in the .vscode/ directory. Comments make a config that changes rarely but needs to be understood when it changes.
Use JSON5 when you’ve deliberately chosen to trade portability for ergonomics. A team’s internal build config, test fixtures with a lot of repeated structure, anything where the convenience of trailing commas and unquoted keys outweighs the cost of requiring a non-standard parser. Be explicit: the file extension should be .json5 so editors pick the right syntax highlighting.
Avoid mixing. The ugly case is a file named config.json that uses JSON5 features. Someone’s going to try JSON.parse on it and crash. Give the file a name that tells readers what dialect it is.
Converting between formats
If you receive a JSONC file and need strict JSON (for example, pasting into a system that requires it), strip the comments and remove any stray trailing commas:
import { stripJsonComments } from 'strip-json-comments';
const strict = stripJsonComments(jsoncSource).replace(/,(\s*[\]}])/g, '$1');
JSON.parse(strict); // safe
If you need to convert structured JSON into another serialization entirely, our JSON to YAML and JSON to CSV converters handle the common ones. For the inverse (TOML configs into JSON for a JavaScript tool chain), try the TOML to JSON converter.
The practical takeaway
The three formats sit at different points on the “human-editable vs. machine-readable” axis. Plain JSON is the only one you can count on across language boundaries. JSONC and JSON5 are editor conveniences — useful where they apply, unhelpful where they don’t.
When a parse error happens, the first question isn’t “is my JSON wrong?” It’s “is this actually JSON, or am I feeding JSON5 to a strict parser?” That one reframe resolves most of the daily friction.
Tools mentioned in this article
- JSON Formatter — Format, validate and minify JSON with syntax highlighting.
- JSON to CSV Converter — Convert JSON arrays to CSV format with custom delimiters.
- JSON to YAML Converter — Convert JSON to YAML format instantly.
- TOML to JSON Converter — Convert TOML configuration to JSON format.