TOML Format & Validate
Validate, lint, format and explore TOML 1.0, and convert it to JSON, with exact error locations. Runs in your browser.
Where your data goes
| Using this page | Using the API, MCP or A2A | |
|---|---|---|
| Where it runs | In your browser. The TOML is parsed, checked and re-formatted by JavaScript on this page (the smol-toml parser plus this site's comment-preserving formatter); nothing you paste is sent anywhere. | In a Cloudflare Pages Function at the Cloudflare data centre nearest the caller. |
| What leaves your device | Nothing you enter. After the page and its scripts load, no request
carries your input. The browser enforces this: the page's Content-Security-Policy
sets connect-src 'none' and form-action 'none', which
block fetch, XHR, WebSocket, EventSource, beacons and form posts.
| The server receives the TOML text, processes it in memory inside the request, and returns the result. Your input is not stored or logged; only the per-call metadata row (operation, time, status, sizes) is recorded. The server makes no network requests. For confidential documents use the page, which does the same work in your browser and sends nothing. The server receives the JSON text, processes it in memory inside the request, and returns the result. Your input is not stored or logged; only the per-call metadata row (operation, time, status, sizes) is recorded. The server makes no network requests. For confidential documents use the page, which does the same work in your browser and sends nothing. |
| Stored | Nothing leaves the page. No cookies, analytics or third-party scripts; the browser keeps only your View → Inline config choice. | Your inputs and results: never. What is stored is your client record (client ID, a SHA-256 hash of your secret, expiry, and the profile you registered with) and one metadata row per call: operation, time, transport, status, error code, duration, request and response sizes, and country. |
| Logged | Nothing (no requests are made). | Only the per-call metadata above, attributed to your client ID. Never parameters, bodies, results, or credentials. Cloudflare, as the host, also processes request metadata (IP address, full URL including the query string, headers) under its own policies, so put inputs in POST bodies. |
| Access | Open to anyone. | Requires a client ID and secret (how to get one). HTTPS only; TLS terminates at Cloudflare. |
Details and how to verify this yourself: Security & privacy.
User guide
The studio works like a desktop window: a menu bar (File, Edit, View, Settings, Help) with the full-screen icon at the right of the same row. Settings → Options… opens the options in an overlay (or View → Inline config shows them in the card), and Help → Keyboard shortcuts… lists the keys.
- Paste or type TOML in the Input view, or choose File → Load sample to start from an example. The editor numbers its lines, and the document is checked as you type.
- Read the verdict. The Problems view (the button at the right of the status line, which shows the count) lists every finding with its line, column, rule and a short explanation. Errors make the document invalid; warnings and notes are about style. Click a finding to jump to that spot in the input.
- Choose Edit → Format (or press Ctrl+Enter). The Formatted view shows the canonical layout; Use as input copies it back into the editor so you can keep working on it.
- Open the Tree view to browse the data as it will be read (see the value mapping below). Every table and array can be collapsed, and each entry shows its type and its path.
- Copy or Download the result, or File → Clear input to start again. The full-screen icon at the right of the menu bar makes the studio fill the window; Esc returns.
Settings → Options… sets how the output looks: the indent for arrays broken over several lines, the line length after which a long array is broken (and longer lines are reported), and sort keys to order the keys in each table.
Formatting keeps your comments. Most TOML tools re-serialise the parsed data and lose every comment. This one rewrites the layout around them: comments on their own line, at the end of a line, and inside multi-line arrays all stay where they were, attached to the same key or element. Strings, numbers and dates are copied exactly as written, so 1_000, 0xDEAD_BEEF, 1.0 and a literal 'C:\path' survive unchanged.
What the checks catch. Beyond outright syntax errors, the usual TOML surprises are a key or [table] defined twice (often a [table] header that collides with a dotted key such as a.b = 1), an integer too large for 64 bits, and syntax from the TOML 1.1 draft that some parsers accept and others reject: line breaks or a trailing comma inside { inline tables }, the \e and \xHH escapes, and times without seconds (07:32). Mistakes from other formats get a hint: True (booleans are lowercase), null (TOML has none) and unquoted strings.
Converting. The same data can be turned into JSON, or JSON into TOML, through the API (see below). JSON has no comments, dates or 64-bit integers, so the conversion follows the value mapping in the specification.
Specification
Grammar. TOML 1.0.0. The parser is smol-toml, which also accepts some TOML 1.1 syntax; that syntax is reported as a warning (rule toml-1-1-syntax) so the document stays readable by TOML 1.0 parsers. A UTF-8 byte order mark is accepted with a warning. Only the first syntax error is reported, because after a syntax error the rest of the text has no reliable structure.
Positions. line and column are 1-based, and columns count Unicode code points, so an emoji counts as one character. offset is 0-based in UTF-16 code units (what a browser text box uses), so the editor can select the spot exactly. LF, CRLF and a lone CR each end a line.
Rules. Severity error makes the document invalid; warning and info do not. “Format fixes” means the formatted output no longer has the problem.
| Rule | Severity | What it checks | Format fixes |
|---|---|---|---|
syntax | error | The text is TOML. The message names the problem and, for common mistakes, how to fix it. | No |
duplicate-keys | error | Each key and table is defined once: no repeated key in a table or inline table, no repeated [table] header, no [table] that reopens a table made by dotted keys, an inline table, a value or an [[array]], and no dotted key that extends a table defined elsewhere. | No |
integer-range | error | Integers fit in 64 bits (−9,223,372,036,854,775,808 to 9,223,372,036,854,775,807), as TOML 1.0 requires. smol-toml would otherwise accept larger ones. | No |
toml-1-1-syntax | warning | No TOML 1.1-only syntax: line breaks, comments or a trailing comma inside an inline table; \e and \xHH escapes; times and date-times without seconds. | Yes, except comments inside an inline table, which stay (one entry per line) because removing them would lose them |
inconsistent-indentation | warning | Keys under the same table header (or at the top) all have the same indentation. Consistent indentation is not reported, but Format removes it. | Yes |
trailing-spaces | warning | No spaces or tabs at the end of a line. Spaces inside a multi-line string are part of its value and are not reported. | Yes |
final-newline | warning | The text ends with exactly one line break. | Yes |
line-endings | warning | All line breaks use the same style. | Yes, unless a multi-line string contains the other style (its value is kept) |
byte-order-mark | warning | No U+FEFF at the start. | Yes |
unnecessary-quotes | info | A quoted key (or part of a dotted key) that could be bare: only A–Z, a–z, 0–9, _ and -. | Yes |
table-order | info | A table is not defined after its own sub-tables ([a.b] then [a]), and the sub-tables of one table are not split up by unrelated tables ([a.x], [b], [a.y]). Both are legal TOML but hard to read. Sub-tables of each [[array]] element are tracked per element. | No (tables are never reordered) |
long-inline-table | info | An inline table is at most 80 characters when written on one line; a [table] section reads better. Only the outermost of nested inline tables is reported. | No |
empty-table | info | A [table] or [[array]] header has keys or sub-tables under it. x = {} is not reported. | No |
multiline-string-crlf | info | A multi-line string contains CRLF line breaks. They are part of the value here, but TOML lets parsers normalise them to LF, so other tools may read a different string. | No |
line-length | info | Lines are at most the configured length (default 120 characters; 0 or none turns the check off). | No |
Formatting.
- One
key = valueper line with one space either side of=, no indentation, and no trailing spaces. A comment at the end of a line follows one space. - Dotted keys are written without spaces around the dots; quotes that are not needed are removed.
- Blank lines: none at the start or end, at most one in a row, none directly after a table header, and exactly one before each header. Comments directly above a header (no blank line between) stay with the header.
- Arrays stay on one line unless they were already written over several lines, contain comments, or would make the line longer than the line length; then each element goes on its own line, indented by the indent setting per level, with a trailing comma. Blank lines between elements are kept (at most one).
- Inline tables are written
{ a = 1, b = 2 }on one line (an empty one as{}). - TOML 1.1 syntax is rewritten to TOML 1.0:
\ebecomes\u001B,\xHHbecomes\u00HH,07:32becomes07:32:00. - The output ends with one line break. A document that uses only CRLF stays CRLF; anything else becomes LF. A byte order mark is removed.
- Sort keys orders the keys of each table by UTF-16 code units, within each group of lines separated by blank lines; a comment directly above a key moves with it, and comments at the very top of the document stay there. Keys inside inline tables are sorted too. Tables themselves are never reordered: the order of
[[array]]elements is data, and sub-tables belong to the element above them. - Strings (including multi-line strings, byte for byte), numbers and dates are copied as written.
- The formatted document always parses to the same data as the input. This is checked on every format: if it ever did not, the input would be returned unchanged.
Value mapping to JSON (the Tree, value and TOML → JSON).
| TOML | JSON |
|---|---|
| String, boolean | The same. |
| Table, inline table | Object, keys in document order. |
| Array, array of tables | Array. |
| Integer within ±(2^53 − 1) | Number (0xff → 255). |
| Integer beyond ±(2^53 − 1) | Decimal string, every digit kept: 9223372036854775807 → "9223372036854775807". |
| Float | Number (1.0 → 1, -0.0 → 0); inf, -inf, nan → the strings "inf", "-inf", "nan". |
| Offset date-time | ISO 8601 string with the offset as written and milliseconds: 1979-05-27 07:32:00-08:00 → "1979-05-27T07:32:00.000-08:00". |
| Local date-time, date, time | The TOML text, with T and milliseconds: "1979-05-27T07:32:00.000", "1979-05-27", "07:32:00.000". |
Fractions of a second beyond milliseconds are truncated (.999999 → .999). A key named __proto__ is an ordinary key.
JSON → TOML. The top level must be an object. Strings, booleans and numbers become key = value; numbers are copied as written, so 1.0 stays a float. Non-empty objects become [table] sections and non-empty arrays whose elements are all objects become [[array]] tables; objects inside other arrays become inline tables, and an empty object is {}. In each table the plain keys come first, then the sub-tables, so the key order can change. A null is an error naming its JSON Pointer (TOML has no null); so are an integer beyond 64 bits and a string with an unpaired surrogate. Duplicate JSON keys keep the last value. The output is formatted as above.
Statistics. tables counts tables below the root ([table] sections, dotted-key tables, inline tables and each [[array]] element); keys counts keys in all tables; arrays counts arrays, including arrays of tables; depth is 1 for a flat document and one more per level of table or array.
Not checked. Whether keys or values make sense for any particular program (Cargo, pyproject, Hugo and so on: use a schema for that); the order of keys; naming style (snake_case versus kebab-case); and whether a float or date can be represented exactly by the program that reads it.
API.
| Endpoint | Input limit | Parameters |
|---|---|---|
POST /api/toml/format | 50,000 characters | input, indent (1–8, default 2), max_line_length (0–1000, default 120; 0 turns wrapping and line-length off), sort_keys |
POST /api/convert/toml-to-json | 200,000 characters | input, indent (0–8 spaces, default 2; 0 is minified) |
POST /api/convert/json-to-toml | 200,000 characters | input, indent, max_line_length, sort_keys |
input can be a string field or a raw text/plain body. /api/toml/format returns valid, summary (counts of errors, warnings and infos), diagnostics, formatted, value and stats. An invalid document is a 200 response with valid: false and formatted and value set to null, not an HTTP error. The conversions return 422 unprocessable_input with the line and column (or JSON Pointer) when the input cannot be converted. The page has no size limit.
Lint rules
| Rule | Severity | What it checks | Fixed by Format |
|---|---|---|---|
syntax | error | The text is TOML: every line is a key = value pair, a [table] or [[array]] header, a comment or blank. | no |
duplicate-keys | error | Each key and table is defined once: no repeated key in a table, no repeated [table] header, and no header or dotted key that redefines an existing value or inline table. | no |
integer-range | error | Integers fit in 64 bits (−9223372036854775808 to 9223372036854775807), as TOML 1.0 requires. | no |
toml-1-1-syntax | warning | No TOML 1.1 syntax that TOML 1.0 parsers reject: line breaks, comments or a trailing comma inside an inline table, \e and \xHH escapes, times without seconds. Format rewrites all of them except comments inside an inline table. | yes |
inconsistent-indentation | warning | Keys in the same table are indented the same way. | yes |
unnecessary-quotes | info | A quoted key that could be bare (only A–Z, a–z, 0–9, _ and -) is written bare. | yes |
table-order | info | Related tables are kept together: a table is not defined after its own sub-tables, and the sub-tables of one table are not split up by unrelated tables. | no |
long-inline-table | info | Inline tables are at most 80 characters on one line; longer ones read better as a [table] section. | no |
empty-table | info | A [table] or [[array]] header has keys or sub-tables under it. | no |
multiline-string-crlf | info | Multi-line strings use LF line breaks. CRLF inside one becomes part of the value here, but TOML lets parsers normalise it, so other tools may read LF. | no |
trailing-spaces | warning | No spaces or tabs at the end of a line. | yes |
final-newline | warning | The text ends with exactly one line break. | yes |
line-endings | warning | All line breaks use the same style (LF or CRLF). | yes |
byte-order-mark | warning | No byte-order mark (U+FEFF) at the start. | yes |
line-length | info | Lines are at most the configured length (default 120 characters). | no |
Errors make the document invalid. Warnings and information are style findings; the document still parses.
API & MCP
The same logic is available over HTTP and as MCP tools. Unlike this page, these run on the server: see where your data goes.
/api/toml/format Check that text is valid TOML 1.0, list problems with line and column, and re-format it with comments kept. GET POST /api/convert/toml-to-json Convert a TOML 1.0 document to JSON, with line and column for any error. GET POST /api/convert/json-to-toml Convert a JSON object to a TOML 1.0 document.