The short answer
Douglas Crockford, who specified JSON, removed comments deliberately. His stated reason was that people had started putting parsing directives in them, which would have broken interoperability β the one property the format exists to have.
So //, /* */, and # are all syntax errors, in every conforming parser, in every language. There is no flag to turn them on because the grammar has no production for them.
What happens if you try
The error is rarely the word "comment", which is why this trips people up. The parser reaches the / where it expected a comma or a closing brace and reports that instead.
| Written | Result |
|---|---|
// note or # note | Syntax error |
/* note */ | Syntax error |
{"port": 8080,} | Syntax error: trailing comma |
{'port': 8080} | Syntax error: single quotes |
{port: 8080} | Syntax error: unquoted key |
Input
{
"port": 8080 // the port
}Error
Expected ',' or '}' after property value in JSON at position 17 (line 2 column 16)
/. Nothing in the message mentions comments, so the usual reaction is to go looking for a missing comma.JSON5 and JSONC
Two supersets exist, and the difference between them matters when you pick one. Neither is JSON: a file written in either will be rejected by JSON.parse and by every strict parser.
- JSONC is JSON plus comments and trailing commas. Nothing else. It is what VS Code uses for
settings.json, and what the TypeScript compiler accepts intsconfig.jsonβ which is why comments work there and people conclude, reasonably, that JSON allows them. - JSON5 goes considerably further: unquoted keys, single quotes, hex numbers, leading and trailing decimal points,
InfinityandNaN, multi-line strings. Closer to a JavaScript object literal than to JSON. - Neither has a registered media type. There is no
application/json5, so these are file formats for tools you control, not wire formats for an API.
| Config file you own | API request or response | |
|---|---|---|
| JSON | Yes | Yes |
| JSONC | If the reader supports it | No |
| JSON5 | If the reader supports it | No |
The four workarounds
Ranked by how well they hold up. The first two are fine; the third has a real cost; the fourth is a trap.
- 1. Use a format that has comments. If the file is configuration a human maintains, YAML and TOML both support
#comments as part of the actual grammar. This is the fix, not a workaround β a config file has no reason to be JSON. - 2. Strip comments before parsing. Standard for JSONC-style config: a small strip pass, then
JSON.parse. Use a real JSONC parser rather than a regex, because a regex will happily destroy a//that appears inside a string value such as a URL. - 3. A sidecar key. Legal JSON, and sometimes the only option when the file must stay strict. The cost is that it is now data: it ships to clients, appears in diffs of the parsed object, and any schema with
additionalProperties: falsewill reject it. - 4. Do not comment out a block by adding a key like `"_disabled"`. The block is still parsed, still present, and the next person will not know which keys the marker was meant to cover. Delete it; the history is in version control.
The sidecar-key approach
{
"_comment": "port must match the load balancer",
"port": 8080
}_comment is now a field in your data, and a strict schema will reject it.Converting to YAML for the comments
If the goal is a config file a human can annotate, converting the JSON to YAML once and then adding comments by hand is the clean path. Going the other direction is where the loss happens, and it is worth seeing exactly how.
- Full-line `#` comments are dropped. Correct behaviour: there is nowhere in JSON to put them.
- A trailing `#` on an unquoted scalar becomes part of the value.
hostcame back as"localhost # trailing comment". YAML's own spec requires a space before an inline comment and treats it as a comment, so this is a limitation of the converter, not of YAML. Quote the value or put the comment on its own line and it round-trips cleanly. - So treat the conversion as one-way. Generate YAML from JSON, annotate the YAML, and keep the YAML as the source of truth rather than converting back and forth.
Commented YAML
# the port the server binds to port: 8080 host: localhost # trailing comment features: # experimental, off by default - search - export
Converted to JSON
{
"port": 8080,
"host": "localhost # trailing comment",
"features": [
"search",
"export"
]
}host.What to do
The decision is really about what the file is for. Data moving between machines does not need comments. A file a person edits does, and that file should not have been JSON.
- API payloads: no comments, no supersets. Document the fields in a schema or in your API docs, where the reader will actually look.
- A config read by one tool you control: JSONC if the tool supports it, otherwise strip-then-parse.
- A config several people edit: move it to YAML or TOML and stop fighting the format.
- Anything you need to explain in place: if the explanation matters enough to write down, it matters enough to go in a schema
description, which tooling can actually show to the reader.
Try it, or read further
- JSON to YAMLConvert once, then add the comments YAML supports natively.
- JSON vs YAMLWhich one a given file should have been in the first place.
- JSON escapingThe other rule the spec enforces that surprises people.
- Complete JSON guideThe full grammar, in the order you need it.