Why JSON Has No Comments and What People Do Instead
JSON has no comment syntax by design, so a // note fails in every parser tested. What people do instead: _comment keys, $comment, JSONC, JSON5 and YAML.

Open a package.json and try to add a note above a dependency. A line starting with // makes the file fail to parse, and npm tells you the JSON is invalid. Open tsconfig.json in the same project and the same line works, because that file is not JSON at all, it is a relative called JSONC that happens to have the same extension. The difference confuses everyone at some point, and the reason is a design decision from the early days of the format that has never been reversed: standard JSON has no comments, and every strict parser rejects them. This article explains why, confirms it with five parsers, and compares the ways people live with it, from a convention in the data to a different format altogether. The wider picture of what parsers accept is in what JSON allows and what parsers quietly accept.
What the parsers say
The input is two lines, a comment and an object:
// note
{"a": 1}Every parser I tried rejected it, and each said so in its own words. Python raised JSONDecodeError: Expecting value: line 1 column 1 (char 0), pointing at the first /. Node's JSON.parse said Unexpected token '/' and quoted the input as not valid JSON. PHP's json_decode returned an error code 4, Syntax error. jq 1.6 reported Invalid numeric literal at line 1, column 3. DuckDB's json_valid returned false. A comment placed after the value fails too: {"a": 1} // note produced "Extra data" in Python, because the parser finished the object and then found more text. Block comments, /* ... */, fail the same way.
There is no parser setting that makes a standards-following parser accept comments, and no accident that makes it happen: the grammar has no place for them. Some tools do accept them, by design, and that is why the confusion arises: an editor that parses settings.json leniently will show no error, and a different program reading the same file will.
Why it was left out
Douglas Crockford, who specified the format, has explained that he took comments out of the first versions after noticing people using them to hold parsing directives, instructions to the parser that would make the same document mean different things to different readers. Since the format's value was that every parser reads the same thing, he removed the feature that could break that. Data exchange formats carry data, he argued, and a comment is not data.
You can disagree with that for configuration files and many people do, and the decision is also old, widely deployed and not going to change. The grammar in RFC 8259 and ECMA-404 does not have comments, and a parser that supports them is supporting an extension.
The common workarounds
People who need notes in JSON have found several approaches, each with trade-offs.
A comment key. Put the note in a field: {"_comment": "keep in sync with the staging file", "timeout": 30}. It works in every parser, because it is ordinary data. The costs: the note is now part of the document, so every consumer receives it and must ignore it; a strict schema with additionalProperties: false rejects it; JSON has no way to attach a comment to a particular value, only to an object; and duplicate _comment keys in one object overwrite each other, since the last wins. Use a name that cannot collide with real fields and be consistent. Some tools define one: // as a key name is used informally in some package files.
**JSON Schema's $comment.** Schemas, which are themselves JSON, have a keyword for this: $comment is ignored by validators and exists for human notes, from draft-07 onward. Inside a schema file, it is the sanctioned place for a comment, and unlike _comment it is part of the vocabulary. It does not help for data files.
Strip comments before parsing. A tool such as jsmin or a regular expression removes // and /* */ and passes the rest to a real parser. It lets you keep comments in a file you author and still hand strict JSON to the consumer. The danger is the regular expression: a naive one removes // inside a string such as "http://example.com", which silently corrupts a URL. Use a real comment-stripping library that understands strings, and test it on a value containing //.
Use a format that has comments. If the file is configuration that people edit, JSON was the wrong container. JSONC, "JSON with comments", is what VS Code uses for its settings files and what TypeScript uses for tsconfig.json: standard JSON plus // and /* */ comments, and often tolerated trailing commas. JSON5 adds comments, trailing commas, unquoted keys, single quotes and more. YAML and TOML have comments as a first-class feature and suit configuration well. JSON versus JSON5 versus JSONC and when to use each compares them.
Choosing between them
A short guide. If the document is exchanged between programs and a human rarely reads it, do not add comments: put the explanation in the documentation or the schema, which can be much longer than a note. If the file is configuration edited by hand, use a format with comments and parse it with a parser for that format, so that "lenient" is a contract and not an accident. If you must annotate JSON that has to remain standard, say a fixture or a feature file consumed by several tools, use a comment key with a reserved name, document it and have every consumer drop it.
Avoid half-measures. A .json file containing comments that works in your editor and fails in CI is a trap for the next person, because the extension says one thing and the content another. Name the file by what it is (.jsonc, .json5) and let tools pick the right parser from the name. If your project uses a loader that strips comments (some bundlers and test runners do), make sure the production path uses the same one, since a config with comments read by the lenient dev loader and the strict production parser is a deployment failure waiting to happen.
Generating, not hand-writing
Many comment needs disappear if the file is generated. A script that writes JSON from a source with comments, a YAML file, a template, a spreadsheet, can keep the notes in the source and emit clean JSON. The generated file says at the top of its documentation, not its contents, that it is generated and where from. That arrangement also fixes the other editing problems of JSON, trailing commas and quoting, because nobody edits the output (trailing commas in JSON and why parsers reject them).
When a comment key is worse than no comment
A comment key is the wrong choice in a few cases. When the consumer is a strict schema, the key fails validation, and you have to teach the schema about it, which spreads the workaround. When the document is signed or hashed, the comment becomes part of what is signed, and a changed note changes the hash. When the file is large and machine-generated, notes in every object bloat it. And when the note is actually documentation of a field's meaning, it belongs in a schema or a README, where it can be found, versioned and translated. The grammar is in RFC 8259, the $comment keyword in the JSON Schema reference, and the JSONC convention in VS Code's documentation of JSON with comments.


