If you've ever opened a Cargo.toml or a pyproject.toml, you've already read TOML. It's the config format that Rust chose for its entire package ecosystem, and it's been quietly spreading to Hugo, Gitea, Forgejo, Taplo, and dozens of other tools. There's a reason developers keep reaching for it instead of YAML or INI.
Where TOML Came From
TOML stands for Tom's Obvious Minimal Language. Tom Preston-Werner — co-founder of GitHub and creator of Semantic Versioning — published the spec in 2013. He was frustrated with existing config formats: INI was too loose and inconsistent, XML was verbose and painful, and YAML had too many footguns hiding in its whitespace-sensitive syntax.
His goal was a format that maps unambiguously to a hash table. Read it once, parse it once, no surprises. The current stable version is TOML 1.0.0, finalized in January 2021 after years of community refinement.
TOML Syntax in Practice
The basic building block is a simple key-value pair with an explicit equals sign:
name = "utilitykit"
version = "1.4.2"
debug = false
port = 3001
Every value has a type. Strings use double quotes. Booleans are lowercase true or false. Integers and floats are unquoted. This matters — we'll come back to it.
Tables (Objects)
A table is declared with square brackets and acts like an object or dictionary:
[server]
host = "0.0.0.0"
port = 8080
timeout = 30
[database]
url = "postgres://localhost/myapp"
pool_size = 5
This is roughly equivalent to the JSON:
{
"server": { "host": "0.0.0.0", "port": 8080, "timeout": 30 },
"database": { "url": "postgres://localhost/myapp", "pool_size": 5 }
}
You can also write inline tables on a single line, which is useful for compact data:
point = { x = 1, y = 2 }
Array of Tables
This is where TOML earns its keep. Use double brackets [[...]] to define an array of objects:
[[dependency]]
name = "express"
version = "4.18"
[[dependency]]
name = "sharp"
version = "0.33"
Each [[dependency]] block appends a new object to the dependency array. It reads linearly from top to bottom, which makes diffs much cleaner than their JSON or YAML equivalents.
Types and Literals
TOML's type system is explicit and complete. The supported types are:
- String —
"double quoted",'literal (no escapes)',"""multi-line basic""", or'''multi-line literal''' - Integer —
42,-7,1_000_000(underscores allowed for readability) - Float —
3.14,6.02e23,inf,nan - Boolean —
true,false - Datetime —
2024-01-15T10:30:00Z(RFC 3339, full datetime or date/time only) - Array —
[1, 2, 3]or multi-line - Table —
{ key = "value" }or header syntax
The datetime type is a major practical win. In JSON or YAML, dates are just strings — your application code has to decide whether "2024-01-15" is a date or an arbitrary string, and implicit type coercion can burn you. TOML bakes dates into the spec.
How TOML Compares to YAML
YAML is technically a superset of JSON and has a much larger type system, but that breadth creates real hazards. The classic example:
# YAML: this is the boolean false, not the string "false"
debug: false
# These are also booleans in YAML 1.1:
verbose: yes
enabled: on
YAML 1.1 treated yes, no, on, off, true, and false as booleans. This caused a notorious problem with country codes — NO for Norway would silently become false. YAML 1.2 fixed most of this, but the behavior depended on which parser you were using.
TOML has no implicit type coercion. "yes" is always a string. false is always a boolean. No ambiguity.
YAML also uses significant whitespace for structure, so an indentation mistake silently changes meaning. TOML uses explicit delimiters — structure is harder to accidentally break.
Where YAML still wins: deeply nested or complex documents. TOML tables get verbose at three or four levels of nesting, while YAML's indentation keeps things compact. For small configs, TOML is usually the easier read. For complex document trees, YAML may still be the better fit.
How TOML Compares to INI
INI files predate modern configuration management by decades. The format is widely understood, but it has no standard — every program implements a slightly different dialect. No standard types, no arrays, no nested structure (or a non-standard [section.subsection] hack), and no boolean representation that every parser agrees on.
TOML was designed to look like a well-specified INI format, which is why Rust's Cargo.toml looks familiar to anyone who's read a .ini file. But TOML has a real spec, a real type system, and real nesting.
You can read more about the INI format and how it compares in our INI files overview.
Where TOML Is Actually Used
The highest-profile user is Rust. Every Rust project has a Cargo.toml that declares package metadata, dependencies, build targets, and workspace configuration. The community has normalized TOML as the standard for all tooling in that ecosystem.
Python adopted TOML for pyproject.toml via PEP 518 and PEP 621. This is now the preferred way to specify build systems, dependencies, and tool configuration (replacing the older setup.cfg and setup.py approach).
Hugo — the static site generator — uses TOML for site configuration. So does Gitea. The Go ecosystem more broadly has adopted TOML for many CLI tools.
Taplo is a dedicated TOML language server and formatter, which shows the format has matured enough to have its own IDE tooling ecosystem.
When to Reach for TOML
TOML is a good choice when:
- You're building a CLI tool and want a config file format users can read and edit manually
- Your config has typed values (ports, booleans, dates) and you want the type to be unambiguous
- You want clean diffs on arrays of structured items (use
[[array-of-tables]]) - Your team works in the Rust or Python ecosystems where TOML is already the convention
It's a worse choice when:
- You need deeply nested structures (YAML handles that more compactly)
- You need to serialize arbitrary data from an API (JSON is the universal choice there)
- Your environment only has parsers for JSON or YAML
Validating and Formatting TOML
TOML documents are fairly easy to validate manually because the syntax is explicit, but you can also paste any TOML into a JSON conversion tool to check the parsed structure. Our JSON Formatter can help you inspect the resulting object after conversion, and JSON to YAML is useful if you need to work with both formats simultaneously on a project.
TOML isn't the most popular config format on the internet, but it's probably the most thoughtfully designed. If you're starting a new project and you have a choice, it's worth serious consideration.