YAML is a superset of JSON’s data model: every valid JSON document is also valid YAML, word for word. The difference is not what each format can represent, it is how each one is written and what each one is comfortable to hand-edit.
JSON vs YAML at a glance
| JSON | YAML | |
|---|---|---|
| Comments | Not allowed | # like this |
| Nesting shown by | Braces and brackets | Indentation |
| Quoting strings | Required | Optional for plain scalars |
| Trailing commas | Not allowed | Not applicable, no commas |
| Multi-line strings | Escaped \n only |
Block scalars (|, >) |
| References to other values | Not supported | Anchors and aliases |
| Typical use | API payloads, data interchange | CI config, Kubernetes manifests |
| Parses without ambiguity | Yes | Mostly, with a few known gotchas (below) |
The same data, two ways
{
"name": "web-app",
"version": "1.0.0",
"scripts": { "build": "vite build", "test": "vitest" },
"dependencies": ["react", "vite"]
}
name: web-app
version: 1.0.0
scripts:
build: vite build
test: vitest
dependencies:
- react
- vite
Same document, same data once parsed. YAML drops the braces, brackets, quotes around plain strings and most of the commas, and uses indentation to show nesting instead.
Where they actually differ
Comments. YAML has them, # like this. JSON has none at all, by design;
a .json config file with an explanation next to a setting has to put that
explanation somewhere else, usually a README, because the parser will reject
a // or # line outright. This alone is why hand-maintained configuration
(CI pipelines, Docker Compose, Kubernetes manifests) defaults to YAML: the
person editing it a year later needs to know why a setting is what it is,
and a comment is the only place that survives next to the value.
Whitespace sensitivity. YAML’s indentation is structural, the same way Python’s is. Two spaces instead of four, or a stray tab where a space was expected, changes what the document means or breaks it outright, and the error message for a bad indent is often less specific than a JSON parser’s “unexpected token at line 4, column 12.” JSON’s braces and brackets are explicit, so indentation is cosmetic only, which is part of why JSON is the format machines generate and consume: a script does not care about readability and would rather have unambiguous delimiters.
Multiple ways to write the same value. YAML has a plain scalar, a single
quoted string, a double quoted string, block scalars for multi-line text,
anchors and aliases for repeating a value by reference, and a handful of
implicit type conversions (yes and no used to parse as booleans in
YAML 1.1, which is exactly the kind of surprise that made the “Norway
problem”, a country code NO reading as boolean false, into a
well known YAML gotcha). JSON has one way to write a string, one way to
write a number, and no implicit conversions at all. Fewer ways to write
the same thing is fewer ways to write it wrong, which is the real argument
for JSON in a machine-to-machine payload.
Verbosity for arrays and comments aside, size. Once whitespace is stripped, JSON and YAML compress to roughly comparable sizes for the same data; YAML’s advantage is readability while being edited by a person, not raw byte count.
When to reach for each
- JSON for anything a program generates and another program consumes without a human reading it in between: API payloads, data interchange, anything serialized and deserialized in the same request-response cycle.
- YAML for anything a person hand-edits regularly and needs comments or multi-line strings to stay maintainable: CI configuration, Kubernetes manifests, application config files that ship with inline documentation.
A good number of tools accept either and convert internally, which is usually the more useful move than picking a permanent side: write the config in whichever format the humans maintaining it prefer, and convert to JSON at the point something else needs to consume it programmatically.
Converting between them
The JSON to YAML Converter runs both directions: it
writes anchors, aliases and multi-document streams correctly rather than
flattening everything to plain scalars, and reading YAML back quotes
anything that would not round trip as the type it started as (a version
string like "1.0" stays a string, not the number 1). Nothing you convert
is uploaded; the conversion runs in your browser the same as every tool on
this site.