Developer
JSON vs YAML vs TOML — Which Config Format Should You Use?
JSON vs YAML vs TOML compared — readability, comments, type safety, edge cases. When to pick each for config files, APIs, and data interchange (with examples).
JSON, YAML, and TOML all do the same job: encode structured data — strings, numbers, lists, nested objects — in a plain text file. They differ in three things developers actually care about: readability, strictness, and how often they'll bite you.
This guide compares the three honestly, including the gotchas most tutorials skip.
Quick Answer
| Use case | Use | Why |
|---|---|---|
| Sending data over an API or between programs | JSON | Universal, fast, no whitespace ambiguity |
| App config that humans edit (CI workflows, Kubernetes) | YAML | Readable, supports comments, multi-line strings |
| App config in a Rust/Python/Go project | TOML | Strict, readable, no whitespace traps |
| Storing data in JavaScript / TypeScript | JSON | Native parser, no extra dependency |
| Anything that humans never edit by hand | JSON | Predictable, machine-friendly |
If you're starting a new tool's config file today, TOML is the safest pick. If you're writing infra (Kubernetes, GitHub Actions, Docker Compose), you don't have a choice — those projects standardized on YAML.
The Same Data in All Three Formats
A simple config — server settings with two environments:
JSON
{
"name": "my-app",
"version": "1.2.3",
"environments": {
"dev": {
"host": "localhost",
"port": 3000,
"debug": true
},
"prod": {
"host": "api.example.com",
"port": 443,
"debug": false
}
},
"tags": ["node", "backend"]
}
YAML
name: my-app
version: 1.2.3
environments:
dev:
host: localhost
port: 3000
debug: true
prod:
host: api.example.com
port: 443
debug: false
tags:
- node
- backend
TOML
name = "my-app"
version = "1.2.3"
tags = ["node", "backend"]
[environments.dev]
host = "localhost"
port = 3000
debug = true
[environments.prod]
host = "api.example.com"
port = 443
debug = false
Same data, three different vibes. JSON is dense and rigid. YAML is airy but indentation-sensitive. TOML reads like an INI file with proper types.
What Each One Is Good At
JSON — the universal language
JSON has won as the data interchange format. Every language has a built-in parser. Web browsers parse it natively. It's strict and unambiguous: there's exactly one right way to write any piece of data.
Strengths:
- Universal — works in every language without a dependency.
- Fast — JSON parsers are highly optimized.
- Predictable — no surprise behavior.
- Easy to validate — JSON Schema is mature and well-supported.
Weaknesses:
- No comments. Yes, really. If you need to document a config file, JSON is the wrong tool.
- No trailing commas.
{"a": 1,}is invalid. This causes endless merge conflicts in human-edited JSON. - Verbose — quotes around every key, braces and brackets everywhere.
- No multi-line strings.
YAML — the human-readable one
YAML was designed in 2001 to be more readable than XML and JSON. It uses indentation for hierarchy, which makes simple files look beautiful and complex files look like a minefield.
Strengths:
- Supports comments (
#). - Multi-line strings without escape characters.
- References (anchors and aliases) for DRY configs.
- Used by Kubernetes, Docker Compose, GitHub Actions, Ansible — like it or not, you have to read YAML.
Weaknesses (the YAML gotchas):
- The "Norway problem" —
country: NObecomesfalsein YAML 1.1 becauseNOis a boolean alias. Norway is one of dozens of values that auto-cast incorrectly. - Indentation matters and mixing tabs and spaces breaks things invisibly.
- Quoted vs unquoted strings can mean different types:
version: 1.10is the number1.1;version: "1.10"is the string"1.10". yes/no/on/off/true/falseare all booleans in YAML 1.1 — surprising when you wanted a string.- The YAML 1.1 vs 1.2 spec mismatch — different parsers handle the same file differently.
If you don't believe how bad these gotchas are, search "YAML Norway problem" or "noyaml.com".
TOML — the strict, readable middle ground
TOML (Tom's Obvious, Minimal Language) was created in 2013 specifically to be an unambiguous configuration format. Inspired by INI files. Used by Cargo (Rust), Poetry (Python), pyproject.toml, Hugo, and many CLIs.
Strengths:
- Supports comments.
- Strictly typed — no Norway problem, no auto-cast surprises.
- Indentation is decorative, not significant.
- Strings always need quotes, so type confusion is impossible.
- Easy to read for flat-ish data; explicit
[section]headers make it skimmable.
Weaknesses:
- Deeply nested data gets verbose (
[a.b.c.d]headers everywhere). - Smaller ecosystem than JSON or YAML.
- Two array-of-table syntaxes (
[[items]]vs inline[{...}, {...}]) confuse newcomers. - Not designed for streaming or wire transfer — it's a config format, not a data interchange.
The Gotchas Nobody Warns You About
JSON: "no comments" is real and painful
Many people try // comment in JSON and are surprised it fails. Workarounds:
- Use a
_commentfield:{"_comment": "..." } - Use JSON5 or JSONC (JSON with Comments) — supported by VS Code's
.jsonfiles, but not by standard parsers. - Switch to YAML or TOML.
YAML: the Norway problem
countries:
- US
- GB
- NO # This becomes `false` in YAML 1.1!
- DE
In YAML 1.1, NO, no, No all parse as false. Most modern YAML parsers (Python's PyYAML, JavaScript's js-yaml) default to YAML 1.1 for backward compatibility. The fix is to quote: "NO". The lesson: in YAML, always quote strings that could be misinterpreted.
YAML: indentation must be spaces, not tabs
Even one tab character in an otherwise space-indented YAML file silently breaks parsing — usually with a useless error message about an unexpected character.
TOML: array of tables syntax
# This is an array of tables
[[products]]
name = "T-shirt"
price = 19.99
[[products]]
name = "Mug"
price = 9.99
vs inline:
products = [
{ name = "T-shirt", price = 19.99 },
{ name = "Mug", price = 9.99 }
]
Both are valid; they produce the same data. Mixing them in the same file works but confuses readers.
Performance: When Does Format Speed Matter?
Almost never, but if you care:
| Format | Parse speed (relative) | Notes |
|---|---|---|
| JSON | 1× (baseline) | Highly optimized in every language |
| TOML | ~2-3× slower | Most parsers are pure-language, not C |
| YAML | ~5-10× slower | Complex grammar, lots of edge cases |
If you're parsing a 50 KB config file at startup, none of these matter. If you're parsing millions of small documents per second, only JSON belongs in that conversation — and at that scale you should look at binary formats (Protobuf, MessagePack, CBOR) anyway.
Converting Between Formats
The three formats are almost-but-not-quite interchangeable. JSON is a subset of YAML 1.2 (any valid JSON file is valid YAML), so JSON → YAML is trivial. The other direction (YAML → JSON) requires choices: what to do with comments, anchors, and types that JSON doesn't natively support (dates).
TOML → JSON is straightforward. JSON → TOML requires deciding how to lay out nested structures (sub-tables vs inline).
Frequently Asked Questions
Does JSON support comments?
No. The JSON spec explicitly rejects comments. If you need to comment a config file, your options are:
- Use a
_commentordescriptionfield as a JSON value. - Use a JSON5 / JSONC parser that does allow comments (VS Code uses these for its own configs).
- Switch to YAML or TOML.
Is YAML a superset of JSON?
YAML 1.2 is intended to be a superset — every valid JSON document should be a valid YAML document. In practice, many YAML parsers are still on YAML 1.1, which has different boolean rules (yes/no/on/off as booleans) and would mis-parse some JSON-shaped data. Treat it as "mostly compatible" and test.
Which is best for config files?
For a brand-new project where you control everything: TOML. It avoids YAML's traps and JSON's no-comment problem. If you're targeting an ecosystem that standardized on something else (Kubernetes, Ansible → YAML; npm → JSON; Rust/Python → TOML), match the ecosystem.
Why does Kubernetes use YAML, not JSON?
Because Kubernetes manifests are written by humans, and YAML is easier to read with deeply nested structures. Kubernetes accepts JSON too — kubectl apply -f file.json works — but humans almost always write YAML. The trade-off: every Kubernetes engineer eventually gets bitten by a YAML indentation bug.
Can I use TOML for an API response?
You can, but you shouldn't. TOML was designed for config, not transmission. APIs should use JSON: every HTTP client and language has a built-in JSON parser, and the data is unambiguous. Use TOML for static configuration, JSON for data over the wire.
How do I convert JSON to YAML?
Most languages have a one-liner:
# Python
import yaml, json
yaml.dump(json.loads(json_string))
// Node.js
const yaml = require("js-yaml");
const json = JSON.parse(jsonString);
yaml.dump(json);
Or you can use the JSON to YAML converter — paste JSON in, copy YAML out, no install needed.
How do I convert YAML to JSON?
Reverse of the above:
import yaml, json
json.dumps(yaml.safe_load(yaml_string))
Or use the YAML to JSON converter in your browser.
Is HJSON a thing?
Yes — HJSON ("Human JSON") is a less-strict variant that allows comments, unquoted keys, and trailing commas. It's much less common than the others but has a niche following. Most projects use YAML or TOML instead.
Why is TOML not as popular as JSON or YAML?
TOML is younger (2013 vs JSON 2001, YAML 2001) and was deliberately scoped to configuration files only, not data interchange. JSON dominated the API space first, YAML dominated infra-as-code first, and TOML had to win one config file at a time. It's gained huge ground in the Rust and Python communities and is the format that's still growing fastest.
Try It Yourself
Compare formats in your browser without installing anything:
- JSON formatter — paste, prettify, validate
- JSON to YAML converter
- YAML to JSON converter
- JSON validator — check for syntax errors
- JSON minifier — strip whitespace for transmission
Want to learn more?
Explore free chapter-wise notes with quizzes and code playground
Prefer watching over reading?
Subscribe for free.