jsonJSONCJSON5configcomments

JSON Has No Comments — Here's What to Use Instead

·8 min read·JSON Concepts

Why JSON Has No Comments

JSON was designed by Douglas Crockford in 2001 as a minimal data-interchange format. Comments were deliberately excluded. Crockford's reasoning: he saw developers using comment fields in early JSON configs to add parser directives — effectively turning comments into a second, unstructured instruction channel. Removing comments kept the format clean, unambiguous, and machine-only.

The result: JSON is a data format, not a config format. But developers use it for config anyway, which is why the absence of comments is felt daily in tsconfig.json, package.json, .eslintrc.json, and launch.json.

What Happens If You Add Comments?

json
{
  // Database configuration
  "host": "localhost",
  "port": 5432 /* PostgreSQL default */
}

Every standard JSON parser throws a syntax error immediately:

  • JavaScript: SyntaxError: Unexpected token /
  • Python: json.JSONDecodeError: Expecting property name enclosed in double quotes
  • Go: invalid character '/' looking for beginning of object key string
  • jq: parse error: Invalid numeric literal

Option 1: "_comment" Key (Zero Tooling Required)

The most portable workaround — works with any standard JSON parser:

json
{
  "_comment": "Database config — override with DB_HOST env var in production",
  "host": "localhost",
  "port": 5432,
  "database": "myapp_dev"
}

Multiple comments using an array:

json
{
  "_comments": [
    "Use DB_HOST env var in production",
    "Run: npm run db:migrate after changing schema"
  ],
  "host": "localhost",
  "port": 5432
}

Pros: Valid standard JSON, works in every tool. Cons: Shows up in the parsed data — your app code sees the _comment key. Strip it before using the config:

javascript
function loadConfig(path) {
  const raw = JSON.parse(fs.readFileSync(path, "utf8"));
  // Remove all _comment and _comments keys recursively
  return JSON.parse(JSON.stringify(raw, (key, val) =>
    key.startsWith("_comment") ? undefined : val
  ));
}

Option 2: JSONC — JSON with Comments (VS Code Standard)

JSONC is a superset of JSON that adds // single-line and /* */ block comments. It is used by VS Code for every config file it ships with: settings.json, launch.json, tasks.json, extensions.json, and tsconfig.json.

json
// .vscode/settings.json — JSONC is the official format
{
  // Format on save
  "editor.formatOnSave": true,
  /* Prettier config */
  "prettier.singleQuote": true,
  "prettier.tabWidth": 2,      // 2 spaces
  "[typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  }
}

Where JSONC is used: VS Code settings, tsconfig.json, some Azure DevOps configs, GitHub Copilot config.

Parsing JSONC in Node.js:

javascript
// npm install jsonc-parser
import { parse } from "jsonc-parser";
import { readFileSync } from "fs";

const config = parse(readFileSync("config.jsonc", "utf8"));
console.log(config.port); // comments are stripped, plain object returned

Parsing JSONC in TypeScript config loading:

typescript
// Built into TypeScript compiler — tsconfig.json supports JSONC natively
// The tsc CLI strips comments before parsing

Option 3: JSON5 — The Human-Friendly Superset

JSON5 is a more ambitious superset that adds comments plus several other developer conveniences:

json5
// config.json5
{
  // Single-line comment
  /* Multi-line
     block comment */

  host: "localhost",        // Unquoted keys (like JS object literals)
  port: 8080,
  debug: true,
  tags: [
    "production",
    "v2",                   // Trailing comma in array
  ],
  message: "Hello World",                     // Line continuation in strings
  maxSize: 0xFFFFF,         // Hexadecimal numbers
}

JSON5 additional features vs JSON:

FeatureJSONJSON5
CommentsNoYes (// and /* */)
Trailing commasNoYes
Unquoted keysNoYes (if valid JS identifier)
Single-quoted stringsNoYes
Multi-line stringsNoYes (backslash continuation)
Hex numbersNoYes
Leading/trailing decimalNoYes (.5, 1.)
Positive infinityNoInfinity

Where JSON5 is used: Babel config (babel.config.json5), ESLint (via plugin), some Webpack configs, and projects wanting human-friendly config without switching to YAML.

Parsing JSON5:

javascript
// npm install json5
import JSON5 from "json5";
import { readFileSync } from "fs";

const config = JSON5.parse(readFileSync("config.json5", "utf8"));

Option 4: Switch to YAML or TOML for Config

If you are using JSON purely for configuration that humans edit, consider switching to a format designed for config:

yaml
# config.yaml — YAML supports # comments natively
# Database config — override with DB_HOST env var in production
host: localhost
port: 5432
debug: false
tags:
  - production
  - v2
toml
# config.toml — TOML is designed for config
# Use DB_HOST env var in production
host = "localhost"
port = 5432
debug = false
tags = ["production", "v2"]

YAML is the dominant choice for infrastructure (Kubernetes, Docker Compose, GitHub Actions, Ansible). TOML is preferred in Rust (Cargo.toml) and Python (pyproject.toml).

Format Comparison for Config Files

FormatCommentsTrailing commasLearning curveTooling support
JSONNoNoVery lowUniversal
JSONCYes (//, /* */)NoLowVS Code native, tsc
JSON5YesYesLowNeeds json5 library
YAMLYes (#)N/AMediumUniversal
TOMLYes (#)N/ALow-mediumUniversal

The Right Choice by Use Case

  • Wire format (API, database, cache): Plain JSON — comments have no place in wire format
  • VS Code settings or tsconfig: JSONC — already supported, no extra packages
  • Application config that developers edit: YAML (infrastructure) or TOML (applications)
  • Babel/bundler config needing flexibility: JSON5
  • Quick workaround without adding dependencies: _comment key in plain JSON

Validate your JSON at JSONKit's JSON Validator to confirm your file is comment-free and parse-ready before shipping.

Try JSON Validator

Check your JSONC or JSON5 is comment-free and valid before parsing.