TOML Cheatsheet

Comparison to JSON and YAML

Use this TOML reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

The feature matrix

FeatureTOML 1.0.0JSONYAML 1.2
comments#none#
multi-line strings""" and '''"a\nb" only| and > blocks
trailing commasarrays onlyforbiddenallowed
unquoted keysyes (bare keys)noyes
references / anchorsnono&a / *a
inheritancenono<<: *base
dates as a type4 typesstring only1.1 timestamp
null typenonenullnull / ~ / empty
heterogeneous arraysyesyesyes
non-string keysnonoyes
indentation significantnonoyes
several docs per filenono---
duplicate keyserrorlast wins (varies)varies by parser
implicit type coercionnonenoneheavy
spec length~1,200 lines~15 pages (RFC 8259)~80 pages
streaming friendlynoyesyes
in Python stdlibtomllib (3.11+)jsonno, needs PyYAML

Ambiguity is the real difference

TOML and JSON have no type coercion. A value's type is determined entirely by its syntax, and there is no set of characters that means one thing to one parser and another to the next.

# YAML: every one of these is a trap
country: NO         # false under a 1.1 parser
mode: 0644          # 420 under a 1.1 parser
version: 1.10       # the float 1.1
time: 12:30         # 750 under a 1.1 parser
# TOML: syntax decides, nothing infers
country = "NO"      # a string, always
mode = 0o644        # 420, explicitly octal
version = "1.10"    # a string, always
time = 12:30:00     # a local time, seconds required

TOML also has no duplicate-key ambiguity. Redefining a key or a table is an error, so a config never silently loses a value. YAML's answer varies by parser, and PyYAML's answer is to keep the last one silently.

Where each one wins

SituationPick
a hand-edited config with a flat-ish shapeTOML
deeply nested config (5+ levels)YAML
repeated blocks that should share a baseYAML, for <<: *base
an API request or response bodyJSON
machine-to-machine, anything untrustedJSON
a file a program reads and rewritesJSON, no comments to lose
a project manifest or tool configTOML
Kubernetes or CI pipelinesYAML, that is what the tools take
logs, one record per lineNDJSON

TOML's design goal is a config file that is obvious to a human reading it without the spec open. That trade shows up as verbosity once you nest deeply, which is the one genuine argument for YAML.

The nesting cost

TOML's headers stop paying off around four levels. Compare a Kubernetes-shaped document:

spec:
  template:
    spec:
      containers:
        - name: api
          image: api:1.2
          resources:
            limits:
              cpu: 500m
[[spec.template.spec.containers]]
name = "api"
image = "api:1.2"

[spec.template.spec.containers.resources.limits]
cpu = "500m"

The TOML is valid and produces the same structure, but the second header repeats six segments and gives no visual clue that it belongs to the container above it. This is exactly why Kubernetes is YAML and pyproject.toml is TOML.

Who uses what

EcosystemFormat
Rust (Cargo.toml, rustfmt.toml)TOML
Python packaging (pyproject.toml, uv, ruff)TOML
Go modules (go.mod)its own format
Node (package.json, tsconfig.json)JSON / JSONC
Kubernetes, Helm, AnsibleYAML
GitHub Actions, GitLab CI, CircleCIYAML
Docker ComposeYAML
Netlify, Fly.io, Traefik, HugoTOML (Hugo takes all three)
TerraformHCL

Python packaging moving to TOML in PEP 518 and PEP 621 is the single biggest reason TOML matters today. tomllib landed in the standard library in Python 3.11.

Reading and converting

LanguageRead TOMLWrite TOML
Pythontomllib (3.11+), tomlitomli-w, tomlkit (keeps comments)
Rusttoml, serdetoml
Gogithub.com/BurntSushi/tomlsame
Nodesmol-toml, @iarna/toml@iarna/toml
CLItaplo get, yq -p tomltaplo fmt, yq -o toml
import tomllib

with open("pyproject.toml", "rb") as f:   # note "rb", bytes not text
    cfg = tomllib.load(f)

print(cfg["project"]["name"])
# TOML -> JSON
yq -p toml -o json '.' pyproject.toml
python -c "import tomllib,json,sys; \
  print(json.dumps(tomllib.load(sys.stdin.buffer), default=str))" < pyproject.toml

tomllib.load requires a binary file object. Passing a text-mode file raises TypeError: File must be opened in binary mode. tomllib.loads takes a str, and the standard library ships no TOML writer, so use tomli-w for plain output or tomlkit when you need to edit a file and preserve its comments and formatting.