JSON vs JSON5 vs JSONC: What Each Parser Actually Accepts
Three formats, one file extension, and no agreement on what counts as valid. Measured against the real parsers, including two that disagree while being the same library.
A config file that your editor renders without complaint can still blow up JSON.parse. Both are right. They are reading different languages that share a file extension.
There are three of these languages in common use, and only one of them has a specification that everybody implements the same way.
What strict JSON actually allows
JSON as defined by RFC 8259 is a small subset of JavaScript object literals, and the grammar genuinely does fit on a napkin1: objects with double-quoted string keys, arrays, double-quoted strings, decimal numbers, and the three literals true, false and null.
Everything people trip over follows from that being the whole language.
No comments. Not //, not /* */. Crockford's stated reason for taking them out is that people had started using them to carry parsing directives, which would have destroyed interoperability5. Whether or not you find that convincing, the grammar has no comment production, so a parser that accepts one is not parsing JSON.
No trailing commas. [1, 2, 3,] is invalid, and it is the single most common way a hand-edited file breaks.
Keys are quoted strings. {foo: 1} is JavaScript. {"foo": 1} is JSON.
No hex, no NaN, no Infinity. Worth being precise about this one, because it is often stated backwards: JavaScript will not produce those either. JSON.stringify(NaN) returns the string null, and so does JSON.stringify(Infinity). The values cannot survive a round trip in the first place.
Duplicate keys are undefined behaviour. The RFC says names SHOULD be unique and that behaviour is unpredictable when they are not1. Most parsers keep the last one. Some error.
The strictness is the whole value. When every implementation agrees on the grammar, a file written in Python reads identically in Go.
JSONC has no specification, only defaults
JSONC is what Microsoft calls "JSON with Comments". VS Code opens settings.json, tasks.json, launch.json and everything else in .vscode/ in that mode3.
The usual summary is that JSONC is strict JSON plus two comment syntaxes and nothing else. That summary is wrong, and you can watch it be wrong.
Microsoft's own documentation says the mode "also accepts trailing commas, but they are discouraged and the editor will display a warning"3. TypeScript 5.9.3 reads a tsconfig.json with comments and trailing commas and returns a parsed config with no diagnostics at all. The jsonc-parser package, which is Microsoft's own, rejects the same file with four errors until you pass allowTrailingComma.
So "is this valid JSONC" has no answer without naming the parser and its options. That is the practical difference between JSONC and the other two: JSON has an RFC, JSON5 has a specification, and JSONC has a default.
JSON5 is written down
JSON5 is a much larger extension, and unlike JSONC it is written down2. It adds:
//and/* */comments- Trailing commas in objects and arrays
- Unquoted keys, where the key is a valid identifier
- Single-quoted strings
- Multi-line strings via escaped newlines
- Hexadecimal numbers
- Leading and trailing decimal points, so
.5and5. Infinity,-InfinityandNaN- An explicit leading
+on numbers
The stated goal is a superset of JSON built from ECMAScript 5.1 productions, made easier for humans to write and maintain by hand2. In practice that means valid JSON5 is valid JavaScript you could paste into a .js file.
The reference table
"Depends" is not a hedge in the JSONC column. It is the answer.
| Construct | JSON | JSONC | JSON5 |
|---|---|---|---|
// and /* */ comments | No | Yes | Yes |
| Trailing commas | No | Depends on the parser | Yes |
| Unquoted keys | No | No | Yes |
| Single-quoted strings | No | No | Yes |
| Hex numbers | No | No | Yes |
NaN and Infinity | No | No | Yes |
| Leading or trailing decimal point | No | No | Yes |
Accepted by JSON.parse | Yes | No | No |
| Has a written specification | Yes | No | Yes |
Everything above was checked by running the file through JSON.parse on Node 24.18, jsonc-parser 3.3.1 and json5 on 2026-08-19, rather than copied from another table.
The extension tells you nothing
The advice everyone gives, including the earlier version of this article, is to name JSON5 files .json5 so nobody is surprised. It is good advice. Babel does not take it: babel.config.json and .babelrc.json are parsed as JSON54, extension notwithstanding.
For the specific question of why tsconfig.json tolerates comments while package.json refuses them, that has its own article.
Picking one
Strict JSON when anything other than you reads the file. API responses, exports, logs, schema files. Every language has a fast strict parser and nobody has to ask which dialect you meant.
JSONC when a tool you already use natively supports it, which in practice means VS Code configuration and tsconfig.json. You get comments where comments genuinely earn their place, on a file that changes rarely and needs explaining when it does.
JSON5 when you have deliberately traded portability for ergonomics, and say so in the filename. Internal build config, fixtures with a lot of repeated structure.
The failure case is a file called config.json that quietly uses JSON5 features, because the next person will reach for JSON.parse and get a stack trace with no clue in it.
Reading a file whose dialect you do not know
Paste it into the JSON Dialect Detector : it names the dialect, points at the exact line of every construct strict JSON would reject, and converts the document to plain JSON. Our JSON Formatter runs the strict grammar, so if it errors on a file your editor is happy with, that file is not JSON.
In code, do not strip comments with a regular expression. Use a parser that understands the dialect and hand it the options you actually want:
import { parse } from 'jsonc-parser';
const errors = [];
const value = parse(source, errors, { allowTrailingComma: true });
if (errors.length) throw new Error(`bad config: ${errors.length} problems`);
// `value` is now plain data, safe to re-serialise with JSON.stringify
For moving structured data between serialisations entirely, the JSON to YAML , JSON to CSV and TOML to JSON converters cover the common directions.
When a parse fails, the useful first question is not whether your JSON is wrong. It is which of the three languages the file is written in, and which one the parser was expecting.
Sources
Every number in this article traces to a source below. Where a claim could not be sourced, it was cut rather than softened.
- Primary sourceIETF
The complete JSON grammar, which contains no comment production and no trailing commas, and the statement that behaviour is unpredictable when object names are not unique.
- Primary sourceJSON5
JSON5's full list of additions over JSON and its stated relationship to JSON and to ECMAScript 5.1.
- Primary sourceMicrosoft
That the JSON with Comments mode is used for VS Code's own config files, supports both comment syntaxes, and accepts trailing commas while showing a warning.
- Primary sourceBabel
That babel.config.json and .babelrc.json are parsed as JSON5 despite the .json extension.
- ReportingHacker News
Crockford's stated reason for removing comments, that they were being used to hold parsing directives. Cited from this discussion because the original 2012 Google+ post no longer resolves.
Topics
- JSON
- JSON5
- JSONC
- Parsers
- Configuration
Tools mentioned in this article
- JSON Dialect Detector - Tell whether a file is JSON, JSONC or JSON5 - and convert it to strict JSON.
- 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.
Get new tools by email
New tools and the occasional deep-dive, about once a month. No spam, no sharing your address, unsubscribe in one click.