TOML vs YAML vs JSON: Choosing a Configuration Format
Every new project eventually needs a configuration file, and three formats dominate the choice: TOML, YAML and JSON. They can all represent the same data, so the decision isn't about capability — it's about which trade-offs (comments, strictness, nesting, tooling) fit how the file will actually be read, written and reviewed.
In short
Short answer: reach for TOML when a human edits a flat-ish, fixed-schema config file by hand (pyproject.toml, Cargo.toml); reach for YAML when the config is deeply nested, hand-maintained, and comments matter more than strict typing (Kubernetes, CI pipelines); reach for JSON when a program writes or reads the file, not a person. All three convert into each other losslessly except for comments, TOML's missing null, and YAML's anchors.
The same settings written three ways
Take a small, realistic config — a server port, a database URL, a list of allowed origins, and a nested logging section — and write it in each format. The shapes converge on the same data; what differs is punctuation, and punctuation turns out to matter a lot once a file is edited by hand every week.
TOML reads like an .ini file that grew real types: [headings] introduce tables, key = value pairs are unambiguous, and there's no meaningful whitespace to get wrong. The YAML rendering of the same config appears in the next section, so each format gets its own code block rather than three squeezed into one.
[server]
port = 8080
allowed_origins = ["https://app.example.com", "https://admin.example.com"]
[database]
url = "postgres://db:5432/shop"
[logging]
level = "warn"
format = "json"Where YAML diverges: indentation as structure
YAML drops the [headings] and brackets entirely and uses indentation to carry the nesting — the same information, with roughly a third fewer punctuation characters, and comments allowed anywhere. That readability is also YAML's best-known failure mode: two spaces where a list item needed four silently reattaches a key to the wrong parent, and nothing about the file LOOKS wrong until something downstream reads a value that isn't there.
The same JSON rendering (below, in the types section rather than repeated here) is denser than either: braces and quotes around every key, no comments, and a trailing-comma trap that trips up hand edits. That combination — machine-precise, human-hostile — is exactly why JSON rarely gets used as a config format a person edits directly; it's the format a PROGRAM writes.
server:
port: 8080
allowed_origins:
- https://app.example.com
- https://admin.example.com
database:
url: postgres://db:5432/shop
logging:
level: warn
format: jsonComments — and why JSON lost configuration files
TOML and YAML both support # comments anywhere in the file — the annotation that makes a config file self-documenting ("# staging only, remove before prod") instead of relying on a separate README or a commit message nobody will read again. JSON, per RFC 8259, has no comment syntax at all: a # or // in a .json file is a parse error, full stop, which is the single biggest reason JSON lost the config-file role it briefly held in the mid-2010s (npm's package.json being the enduring exception, kept for historical and tooling-compatibility reasons rather than because JSON is pleasant to hand-edit).
Where JSON-like syntax with comments is genuinely wanted, the answer isn't ad-hoc — it's JSON5 or JSONC (JSON with Comments, the dialect VS Code's own settings.json uses), both supersets of JSON that add comments and a few other conveniences while staying close enough to parse with a relaxed JSON parser. This site's JSON5 Formatter handles exactly that dialect if you're maintaining a JSONC-style file rather than migrating away from JSON entirely.
Types: TOML's first-class dates, YAML's implicit typing, JSON's six values
JSON's type system is the shortest to describe because it's exhaustive: string, number, boolean, null, object, array — six kinds, no more, no native date type, and no integer/float distinction beyond what the value itself looks like. TOML goes the other direction, with the richest built-in type set of the three: strings (four different quoting styles for different escaping needs), 64-bit integers, floats, booleans, and — uniquely — four native date/time types (offset date-time, local date-time, local date, local time), each with its own unambiguous syntax. There is no TOML null; a key either has a value or doesn't exist.
YAML's type system is the one that causes production incidents, because it's IMPLICIT: a bare scalar's type is inferred from its text by a set of resolution rules, and those rules changed between YAML 1.1 and 1.2. The canonical trap — nicknamed the Norway Problem — is a country code of NO: under YAML 1.1's Core Schema (what older parsers like PyYAML's default loader and SnakeYAML implement), the bare word NO resolves to the boolean false, not the string "NO". YAML 1.2 narrowed the set of words that resolve to booleans (only true/false survive unquoted), which fixes the Norway case but means the SAME FILE can parse differently depending on which YAML version reads it — the honest fix, in any version, is to quote a value whenever its bare form could be misread.
| Type | TOML | YAML (1.2 core schema) | JSON |
|---|---|---|---|
| String | Yes — 4 styles | Yes, implicit unless quoted | Yes |
| Integer | Yes, 64-bit | Yes, implicit | Number only (no int/float split) |
| Float | Yes, incl. inf/nan | Yes, implicit | Number only |
| Boolean | true / false only | true/false (1.1 also yes/no/on/off) | true / false |
| null | Not representable | null or ~ | null |
| Date / time | 4 native kinds | Not native — a string unless quoted or a loader extension | Not native — always a string |
| Binary | No | No (base64 as a string, by convention) | No |
Nesting and repetition: tables, indentation, braces
All three represent nested structure and repeated structure, but the ergonomics diverge sharply once a list of objects shows up. TOML's array-of-tables syntax ([[bin]], repeated once per element) reads well for a handful of similarly-shaped records — see the example in the TOML to JSON page — but gets unwieldy past a dozen or so, at which point an inline array of inline tables is usually more compact. YAML's dash-prefixed list items nest naturally under any key at any depth and stay readable even heavily nested, which is exactly why Kubernetes manifests — deeply nested container specs, volume mounts, environment variable lists — are YAML and not TOML. JSON's braces and brackets nest arbitrarily deep with zero ambiguity, at the cost of density; a JSON file five levels deep is a wall of closing braces that YAML's indentation makes visually obvious for free.
YAML alone offers anchors (&name) and aliases (*name) — a way to define a block once and reference it elsewhere without repeating it, which is genuinely useful for Docker Compose service defaults or CI job templates that share most of their configuration. Neither TOML nor JSON has an equivalent; converting FROM YAML to either format always resolves anchors into their full expanded content, because there's nowhere else for the reference to go.
Who standardized on what
The format a tool chose is rarely arbitrary — it usually reflects exactly the trade-offs above: does a human write this file by hand, does it need comments, does it nest deeply, does the ecosystem already have strong tooling in one format.
| File | Format | Why (roughly) |
|---|---|---|
| pyproject.toml | TOML | PEP 518 chose TOML explicitly for Python packaging metadata — see the FAQ below |
| Cargo.toml | TOML | Rust's package manifest; flat-ish, hand-edited, fixed schema |
| wrangler.toml | TOML | Cloudflare Workers configuration |
| netlify.toml | TOML | Netlify build/deploy configuration |
| Hugo site config | TOML (or YAML/JSON) | Hugo accepts all three; TOML is the historical default |
| Kubernetes manifests | YAML | Deep nesting, hand-maintained, comments valued |
| GitHub Actions workflows | YAML | Nested job/step structure, readability under version control |
| Docker Compose files | YAML | Anchors for shared service defaults, deep nesting |
| Ansible playbooks | YAML | Human-authored, list-heavy, comment-friendly |
| OpenAPI specs | YAML or JSON | Both accepted; YAML preferred for hand-authored specs |
| package.json, tsconfig.json | JSON | Written and read by tooling far more often than by a person |
| ESLint flat config | JavaScript (not JSON/YAML/TOML) | Modern ESLint moved to a real JS module for full expressiveness |
| .vscode/settings.json | JSONC (JSON with comments) | VS Code's own settings accept comments despite the .json extension |
| Gemfile | Ruby DSL (not YAML) | Ruby's own Bundler format is executable Ruby, not a data format |
INI and .properties: the older cousins
Before TOML formalized the idea, plain INI files (section headers in [brackets], flat key=value pairs, no real nesting, no standardized spec) covered similar ground with far less precision — no defined types, no defined nesting, and every parser implementing its own dialect. TOML is best understood as "INI, but with an actual specification, real types, and a defined way to nest." Java's .properties format is the other flat-file cousin: strictly key=value, no sections at all, nesting simulated by convention through dotted keys (server.port=8080), and Spring Boot's own YAML support exists specifically because .properties' flatness becomes painful past a few dozen keys.
Converting between YAML's real nesting and .properties' dotted-key convention is common enough in the Spring Boot world that it's its own pair of tools here: YAML to Properties flattens a nested application.yml into dotted keys, and Properties to YAML reverses it, rebuilding real nesting (and even keeping your # comments, which the TOML and JSON converters below cannot do, since neither format's grammar attaches comments to the parsed value the way this dedicated flattening logic can).
A decision guide
None of these formats is objectively better — each optimizes for a different reader. The table below is the practical version of everything above.
| Choose… | When… | The failure mode you accept |
|---|---|---|
| TOML | A human edits a flat-to-moderately-nested config with a fixed schema, and unambiguous typing matters (versions, dates, package metadata) | Deep nesting gets verbose; no comments-with-reserialization if a tool ever rewrites the file |
| YAML | The config is deeply nested, hand-maintained, comments matter, and you can enforce quoting discipline (or a linter) against implicit typing | The Norway Problem and indentation-as-syntax bugs, if discipline slips |
| JSON | A program writes and/or reads the file — an API response, a lockfile, a build artifact — and a human rarely edits it directly | No comments, no trailing commas, verbose for a human to hand-maintain |
Converting between them without losing meaning
Every conversion between these formats is lossy in a specific, predictable way, and knowing which loss applies to your direction is the difference between a confident migration and a surprised bug report. YAML → anything loses comments (nothing else has them) and resolves anchors/aliases into their full expanded content. Anything → TOML loses null (there's nothing to convert it to, so it's omitted or rejected, your choice — see JSON to TOML). TOML → JSON keeps everything except the format of dates (which become plain ISO strings) and needs a string fallback for 64-bit integers past what a JSON number can hold exactly (see TOML to JSON).
The tools on this site that make these specific conversions: TOML to JSON and JSON to TOML for the TOML/JSON pair, TOML Formatter to validate or normalize a TOML file on its own, and YAML to Properties / Properties to YAML for the Spring Boot–style flattening pair discussed above. For the YAML/JSON pair specifically — not covered by this guide in depth since it's the more established of the three — see JSON to YAML, YAML to JSON, and the general YAML vs JSON comparison.
Sources
Frequently asked questions
Is TOML a superset or subset of JSON?
Neither, exactly — they're siblings that both aim at the same underlying data model (a tree of strings, numbers, booleans, arrays and key-value tables) through different syntax, and neither's SYNTAX is valid in the other. In terms of what each can EXPRESS: every TOML document maps onto an equivalent JSON value (with dates flattened to strings and null simply absent, since TOML has none), but the reverse doesn't hold — a JSON document containing null, or a top-level array rather than an object, has no direct TOML equivalent, which is exactly the constraint the [JSON to TOML](/json-to-toml) converter on this site has to work around.
Why did Python choose TOML for pyproject.toml?
PEP 518, which introduced pyproject.toml, states TOML's advantages directly: it's a fully specified, easily human-editable format designed specifically for configuration (unlike JSON, which PEP 518 notes is "not fun to write by hand" and has no comments), and it's simpler and more constrained than YAML, avoiding YAML's implicit-typing ambiguity and the security concerns some YAML loaders have historically carried. The PEP also weighed inventing a new Python-specific format and rejected it in favor of an existing, already-specified one — TOML was, at the time, the closest fit to "INI file with real types and a spec."
Can TOML represent null?
No — there's no null keyword or equivalent anywhere in the TOML 1.0 grammar; a table either has a key with a real value, or the key simply isn't present. This is a deliberate simplicity choice, not an oversight, and it's the reason [JSON to TOML](/json-to-toml) on this site has to make an explicit decision about every null value it encounters: omit the key (the default, with a count reported) or fail the conversion outright so you notice the null before it silently disappears.
Which of the three parses fastest?
JSON, by a wide margin — its grammar is small, unambiguous, and every mainstream language ships a highly optimized native parser (V8's JSON.parse is close to hand-written-C speed). TOML parses close behind JSON, since its grammar is similarly compact even with the richer type set. YAML is reliably the slowest of the three and the most complex to implement correctly — full YAML 1.2 support means handling anchors, multiple document streams, several scalar styles, and a much larger grammar overall. In practice this almost never matters for a config file read once at startup; it matters far more for a format used as a wire protocol for many small, frequent messages, which is a large part of why JSON (not YAML) is what APIs actually speak.
Does TOML have multi-line strings and comments?
Yes to both. Comments start with # and run to the end of the line, exactly like YAML and Python. Multi-line strings come in two flavors: """triple double-quoted""" strings process escape sequences the same as a regular basic string (and a leading newline right after the opening """ is trimmed, a convenience for formatting), while '''triple single-quoted''' literal strings take their content completely as-is with zero escaping, which is the natural choice for a regex pattern or a Windows path that's full of backslashes you don't want interpreted.
Paste a TOML file and get clean JSON in your browser — dates, 64-bit integers and arrays of tables all handled correctly.
Convert TOML nowKeep working
Related tools
TOML to JSON
Parse any TOML document — pyproject.toml, Cargo.toml, wrangler.toml — into clean JSON, with dates kept as ISO strings and 64-bit integers kept exact.
JSON to TOML
Turn a JSON object into well-formed TOML — tables from nested objects, [[tables]] from arrays of objects, and an honest report of what a null cost you.
TOML Formatter
Validate TOML with exact error positions and your comments intact, or normalize it into a canonical layout — the two are deliberately different modes.
YAML to Properties
Flatten application.yml into application.properties — dotted paths, Spring's key[0] list convention, and the ISO-8859-1 \uXXXX escaping Java's Properties loader expects.
Properties to YAML
Rebuild application.properties into application.yml — dotted keys unflattened into real nesting, key[0] lists reassembled, and your # comments kept.
JSON to YAML
Re-express JSON as clean, human-friendly YAML — smart quoting, two-space indentation, list dashes done right.
YAML to JSON
Full YAML 1.2 parsing — anchors resolved, types preserved, errors pinpointed — straight into clean JSON.
JSON Formatter
Format, beautify, and validate JSON instantly in a professional VS Code-style editor — free, fast, and 100% private.
YAML Formatter
Normalize any YAML to clean, consistent style — uniform indentation, canonical quoting, validation built in.
JSON5 Formatter
Beautify JSON5 the way it's meant to be written — unquoted keys, trailing commas and all, with real JSON5 parsing.