MarkupSuite

TOML Explained: Config Files Without Surprises

5 min readUpdated September 2026
A developer working at a keyboard

TOML — Tom's Obvious, Minimal Language — was created by GitHub co-founder Tom Preston-Werner as a reaction to YAML's ambiguities. Its guiding idea is that a configuration file should have exactly one obvious way to write each thing, with no implicit type coercion and no whitespace traps. The result is a format that is a little more verbose than YAML but far more predictable. Version 1.0.0 landed in January 2021, and the specification has been stable since, which is part of why ecosystems felt safe adopting it.

Keys, values and explicit types

At the top level, TOML is a set of key = value pairs. Every value has an unambiguous type decided by its syntax: strings sit in double quotes, numbers are written bare, booleans are true or false, and dates use the RFC 3339 format. There is no guessing — port = 8080 is always an integer and port = "8080" is always a string. That strictness is TOML's core selling point over YAML, where the same text could be read either way.

The rules around keys are just as tight. A bare key may contain letters, digits, underscores and dashes; anything else has to be quoted, and a quoted key may hold any character at all. Defining the same key twice anywhere in a document is an error rather than a last-one-wins overwrite, so a badly resolved merge conflict fails loudly instead of changing behaviour in silence. Whitespace around the equals sign is free, but a key and its value must share a line — there is no line continuation, and indentation carries no meaning anywhere in the format.

Strings, numbers, dates and arrays

Strings come in four flavours: basic strings in double quotes with backslash escapes, literal strings in single quotes where nothing is escaped — ideal for Windows paths and regular expressions — and multi-line versions of both written with triple quotes. Numbers may carry underscores for readability, as in 1_000_000, and accept hexadecimal, octal and binary through the 0x, 0o and 0b prefixes. Integers must be handled as at least 64-bit signed values, so a large identifier survives where a JavaScript JSON parser would round it. Dates and times follow RFC 3339 in four varieties — offset date-time, local date-time, local date and local time — so a date is a real date rather than a string you parse yourself. Arrays are square-bracketed, may span lines, and may hold mixed types.

Tables and arrays of tables

TOML groups related settings into tables, written as a header in square brackets like [database] followed by that table's keys. Dotted headers such as [server.tls] create nested tables. When you need a list of similar objects — several users, several build profiles — you use an array of tables, written with double brackets [[users]] repeated once per entry. This layout keeps even a large configuration readable, because each section is clearly labelled and self-contained.

Two ordering rules catch people out. A table header applies to every key below it until the next header, so a key you meant for the root ends up inside the last table if you append it at the bottom of the file — the single most common TOML mistake. You also cannot reopen a table you have already defined. For short groups, an inline table written on one line as point = { x = 1, y = 2 } keeps things compact, but an inline table must be fully self-contained, with no newlines inside its braces.

What TOML deliberately leaves out

There is no null. If a setting is absent you omit the key and let the application supply its default, which removes JSON's ambiguity between a missing field and an explicit null but also means you cannot express "deliberately unset". There is no inheritance or merging either, so shared blocks are duplicated or assembled by the tool that reads the file. And while dotted keys and nested tables handle depth, TOML gets awkward past two or three levels: a header like [a.b.c.d] is legal but reads worse than the YAML equivalent. That is the honest boundary — TOML is a configuration language, not a general data-interchange format.

Where TOML shines

TOML has become the default in several major ecosystems. Rust's Cargo.toml declares a crate's metadata, dependencies and build profiles. Python's pyproject.toml, standardised in PEP 518 and extended by PEP 621 for project metadata, is now the central config for Poetry, Hatch, PDM, Ruff and uv — and since Python 3.11 the standard library reads it through tomllib, with no dependency required. Netlify's netlify.toml and the Hugo static-site generator use it as well. In each case the appeal is the same: no surprises when a machine and a human both read the same file.

Open the TOML tab in MarkupSuite to format a file, convert it to JSON to inspect the exact structure a parser sees, or turn a JSON object back into clean TOML. Converting to JSON is the quickest way to confirm that an array of tables produced the list you expected and that a dotted header nested where you thought it would. Everything runs in the browser, so a config full of internal hostnames never leaves your machine.

The takeaway

TOML is key = value pairs with explicit types, grouped into tables and arrays of tables, with duplicate keys treated as errors and no null at all. Choose it when you want configuration that reads the same way to a human and a machine — and format or convert it in MarkupSuite.

Related tool
MarkupSuite · TOML

Open the tool and try it now