Andrew Mercer

Comprehensive Guide to TOML

What It Is

TOML (Tom's Obvious, Minimal Language, created by Tom Preston-Werner, co-founder of GitHub) is a config-file format designed to be easy for humans to read and write, and to map unambiguously to a hash table when parsed. It sits between JSON's strictness and YAML's flexibility — closer in spirit to an INI file, but with real, well-defined types and no ambiguity about nesting.

It's the format behind Rust's Cargo.toml, Python's pyproject.toml (the modern standard for Python packaging metadata), and a number of other tool configs (Hugo's config.toml, some parts of GitLab CI tooling).

Spec: toml.io.

Core Syntax

Key-value pairs

name = "Andrew"
active = true
count = 42
ratio = 3.14

Bare keys can use letters, digits, underscores, and hyphens without quoting. Keys with other characters (spaces, dots meant literally) need quoting: "my key" = "value".

Tables (sections)

[server]
host = "localhost"
port = 8080

[server.tls]
enabled = true
cert_path = "/etc/certs/server.pem"

[server.tls] is a dotted table name — equivalent to nesting tls as an object inside server. You don't need to declare [server] before [server.tls] explicitly appears, though doing so for readability is common.

Arrays

tools = ["terraform", "kubernetes", "elasticsearch"]
mixed_ok = [1, 2, 3]

Arrays can span multiple lines and even mix whitespace/comments per element:

tools = [
  "terraform",  # IaC
  "kubernetes", # orchestration
  "elasticsearch",
]

A trailing comma is allowed in multi-line arrays.

Array of tables

This is TOML's answer to "a list of objects," and the one construct that looks genuinely different from JSON/YAML:

[[servers]]
name = "web1"
ip = "10.0.0.1"

[[servers]]
name = "web2"
ip = "10.0.0.2"

Each [[servers]] header appends a new table to the servers array. This parses equivalently to the JSON:

{ "servers": [
  { "name": "web1", "ip": "10.0.0.1" },
  { "name": "web2", "ip": "10.0.0.2" }
]}

Inline tables

For small, single-line objects:

point = { x = 1, y = 2 }

Note: unlike multi-line tables, inline tables must be written on a single line per the spec (though not all parsers strictly enforce this).

Strings

basic = "A string with \"escapes\" and \n newlines"
literal = 'No escapes here, what you see is what you get \n'
multiline_basic = """
Line one.
Line two.
"""
multiline_literal = '''
Raw text, no escape processing.
'''

Numbers

int_val = 42
negative = -17
float_val = 3.14
exponent = 5e+22
hex_val = 0xDEADBEEF
octal_val = 0o755
binary_val = 0b11010110
underscored = 1_000_000   # underscores allowed for readability

Dates and times — a native type

Unlike JSON and YAML, which treat dates as plain strings, TOML has first-class date/time types conforming to RFC 3339:

offset_datetime = 2026-08-25T14:30:00Z
local_datetime  = 2026-08-25T14:30:00
local_date      = 2026-08-25
local_time      = 14:30:00

A conforming parser hands these back as actual date/time objects, not strings — no manual parsing required on the consuming end.

Comments

# This is a comment, running to end of line
name = "Andrew" # inline comments work too

Gotchas

  • Dotted keys vs. explicit tables express the same nesting two different ways, which can make a hand-edited file look inconsistent:
    toml a.b.c = 1 # is equivalent to: [a.b] c = 1
  • Array-of-tables ([[x]]) is the one construct that trips up newcomers — it's easy to confuse [x] (a single table) with [[x]] (appending to an array of tables), and mixing the two for the same key is invalid.
  • Once a table is "closed" by a later table header, you can't reopen it out of order — all keys for [server] must appear together (or under further-nested [server.x] headers) before the next top-level table begins; TOML doesn't allow scattering a table's keys across the file.
  • Redefining a key is an error, unlike some looser formats that silently take the last value — a conforming TOML parser should reject it outright.
  • Tooling support is thinner than JSON or YAML outside the Rust/Python packaging ecosystems — fewer linters, less universal editor support, though this has been improving steadily.

Comparison to JSON/YAML at a Glance

TOML YAML JSON
Comments Yes Yes No
Native dates Yes No No
Nesting via Table headers / dotted keys Indentation Braces
List-of-objects [[table]] - block sequence [{}, {}]
Ambiguity Low Higher (type coercion surprises) None

Tools

  • toml.io — spec and reference, including a side-by-side comparison with JSON
  • Taplo — TOML linter, formatter, and language server (powers TOML support in rust-analyzer-adjacent tooling and several editors)
  • Native/standard-library support: Rust's toml crate (used throughout the Cargo ecosystem), Python's tomllib (built into the standard library since Python 3.11, read-only; tomli/tomli_w for older versions or writing)
  • Cargo.toml reference — a good real-world example of TOML's array-of-tables and nested-table conventions in active use