JSON vs YAML vs TOML: Which Config Format Should You Use?

Suman SharmaSuman Sharma

All three describe the same thing, nested keys and values. They differ in who they're written for.

  • JSON: written for machines. Strict, unambiguous, no comments.
  • YAML: written for humans, with indentation instead of brackets. Flexible enough to surprise you.
  • TOML: written for humans editing config files. Explicit and boring, which is the point.

The same config, three ways

JSON

{ "name": "api", "port": 8080, "debug": false, "database": { "url": "postgres://localhost/app", "pool": 10 }, "allowedOrigins": ["https://example.com", "https://admin.example.com"] }

YAML

# service config name: api port: 8080 debug: false database: url: postgres://localhost/app pool: 10 allowedOrigins: - https://example.com - https://admin.example.com

TOML

# service config name = "api" port = 8080 debug = false allowedOrigins = ["https://example.com", "https://admin.example.com"] [database] url = "postgres://localhost/app" pool = 10

Side by side

JSONYAMLTOML
CommentsNo (JSONC/JSON5 yes)YesYes
Strings need quotesAlwaysUsually notAlways
Whitespace mattersNoYes, indentation is structureNo
Deep nestingFineFineGets awkward
Type surprisesNoneSeveral (see below)Very few
Native in browsers/JSYesNoNo
Common homesAPIs, package.json, tsconfig.jsonKubernetes, GitHub Actions, Docker ComposeCargo.toml, pyproject.toml, Hugo

The YAML traps

YAML's flexibility is where the bugs come from.

Unquoted values get guessed. Older YAML 1.1 parsers, which are still common, read these as booleans:

country: NO # false, not "NO" (the "Norway problem") enabled: yes # true answer: off # false

Numbers that aren't numbers.

version: 1.10 # the float 1.1, not "1.10" zip: 01234 # may be read as octal or lose the zero time: 12:30 # some parsers read this as a base-60 number

Indentation is meaning. One space off and a key moves to a different parent, often without an error.

Tabs aren't allowed for indentation.

The fix for most of these: quote strings that could look like something else, and run a linter (yamllint) in CI.

When each one wins

Choose JSON when a program writes the file, the data travels over a network, or you need every language to parse it identically. If you need comments in a JSON config, check whether your tool accepts JSONC (tsconfig.json does).

Choose YAML when the ecosystem already uses it. Don't fight Kubernetes or GitHub Actions. Quote strings, lint it, and keep files short.

Choose TOML when you're designing a new config file that people will edit by hand. It's hard to get wrong, has comments, and every value has an obvious type.

Converting between them

yq is jq for YAML (and it reads JSON and TOML too):

yq -o=json config.yaml > config.json yq -p=json -o=yaml config.json > config.yaml

To check JSON is valid before converting, paste it into the JSON Formatter. For querying JSON from the terminal, see the jq tutorial.

Frequently asked questions

Which is better for config files, YAML or TOML?

For config that people edit by hand, TOML is usually safer: it's explicit about types and has fewer surprises. YAML is the better choice when the ecosystem expects it, such as Kubernetes, GitHub Actions or Docker Compose.

Can JSON have comments?

Standard JSON can't. Some tools accept JSONC or JSON5, which allow comments, for example tsconfig.json and VS Code settings. A plain JSON.parse call will reject them.

Is YAML a superset of JSON?

YAML 1.2 is designed to be a superset of JSON, so most JSON documents are valid YAML. In practice, parser differences mean you shouldn't rely on it for edge cases.

Contact

Get in Touch

Want to call me? book a call and I'll respond in the available time slot. I will ignore all soliciting.