TOON Syntax Cheat Sheet: Every Rule on One Page
A one-page TOON reference: objects, lists, tabular arrays, delimiters, quoting, nesting, and edge cases—each with a side-by-side JSON example.
TOON encodes JSON using YAML-style indentation for nesting and CSV-style tables for uniform arrays of objects — with no braces, brackets, or repeated keys. This cheat sheet covers every rule in TOON spec 4.1: objects, primitive arrays, tabular arrays, keyed tables, nested field groups, delimiters, quoting, nesting, numbers, comments, and edge cases. Every TOON example below is real output from @toon-format/toon 4.1.1.
For a deeper introduction, see What Is TOON? and the full TOON specification. To convert files now, use the free JSON-to-TOON converter.
TOON Syntax at a Glance
The table below maps each JSON construct to its TOON equivalent. Every rule is expanded with a code example in the sections that follow.
| Construct | JSON form | TOON form | Key saving |
|---|---|---|---|
| Object | {"k": "v"} | k: v | No braces or quotes |
| Primitive array | ["a","b","c"] | key[3]: a,b,c | One line, length declared |
| Array of objects (uniform) | Array with repeated keys | key[N]{f1,f2}: + rows | Keys declared once; rows are CSV |
| Keyed table (v4) | Object of same-shape objects | flags[3:]{enabled,rollout}: + name: v1,v2 rows | Map keys become row labels |
| Nested field group (v4) | Uniform nested object in each row | orders[2]{id,customer{name,country},total}: | Nested objects stay tabular |
| Non-uniform array | Objects with differing keys | key[N]: + - item lines | No braces or brackets |
| Empty array | [] | key: [] | Canonical since spec 3.1 |
| Nested object | Nested braces | Indented key-value block | No braces at any depth |
| Unquoted string | "active" | active | 2 tokens saved per value |
| Quoted string | "hello, world" | "hello, world" | Quotes only when needed |
| Number | 42 | 42 | Identical |
| Boolean | true / false | true / false | Identical |
| Null | null | null | Identical |
| Comment (v4) | Not supported | # text on its own line only | Full-line only; never inline |
Rule 1: Key-Value Objects (No Braces)
The most fundamental rule: TOON objects drop all curly braces. Each key-value pair is written as key: value on its own line. The colon must be followed by at least one space when a value appears on the same line.
JSON
{
"model": "gpt-5.4-nano",
"temperature": 0.7,
"stream": true
}TOON
model: gpt-5.4-nano
temperature: 0.7
stream: trueThree lines; no opening or closing braces; no quote marks around the key or the bare-word string value. This alone saves roughly 30% of tokens on shallow objects.
Rule 2: Unquoted vs. Quoted Strings
Strings that are plain bare words do not need quotes, saving two tokens per value. Quotes are required in these situations:
- The value contains the active delimiter (comma by default).
- The value starts or ends with whitespace.
- The value could be mistaken for a number (
"42","05","3.14"), a boolean (true,false), ornull. - The value contains a colon, double quote, backslash, or bracket/brace character.
- The value starts with
-or#, or is exactly-. The#rule is new in spec v4 so that strings cannot be mistaken for comments. - The value is an empty string.
JSON
{
"status": "active",
"label": "hello, world",
"flag": "true",
"empty": "",
"note": "#tag",
"count": "42"
}TOON
status: active
label: "hello, world"
flag: "true"
empty: ""
note: "#tag"
count: "42"active is bare because it is unambiguous. hello, world needs quotes because the comma is the default delimiter. true needs quotes to distinguish it from the boolean literal. An empty string is always quoted. #tag is quoted because a line starting with # would read as a comment, and "42" is quoted so it decodes as a string rather than a number. A # in the middle of a value (x#y) needs no quotes.
Rule 3: Primitive Arrays (Inline)
Arrays of primitives (strings, numbers, booleans, null) are written inline on one line: the key, the length in square brackets, a colon, and the values separated by the active delimiter. Dash-prefixed list items are reserved for non-uniform arrays (see Rule 8).
JSON
{
"tags": ["featured", "new", "on-sale"]
}TOON
tags[3]: featured,new,on-saleThe [3] length marker lets a model (or a strict decoder) check that no item is missing. There are no spaces after the commas, and bare-word strings need no quotes.
Rule 4: Tabular Arrays of Objects (The Table Block)
This is TOON's signature feature. When an array contains objects that all share the same keys, TOON replaces the repeated structure with a single header line and comma-separated value rows — like a CSV embedded inside the document.
Header syntax: key[N]{field1,field2,...}:
[N]— the number of rows, giving the LLM an explicit count to validate against.{field1,field2}— the schema: field names declared once for all rows.- Each subsequent indented line is one row of comma-separated values, with no space after the delimiter.
- Since spec 4.1, encoders must use this tabular form whenever every element is an object with the same keys and primitive values.
JSON
{
"users": [
{"id": 1, "name": "Alice", "role": "admin"},
{"id": 2, "name": "Bob", "role": "editor"},
{"id": 3, "name": "Carol", "role": "viewer"}
]
}TOON
users[3]{id,name,role}:
1,Alice,admin
2,Bob,editor
3,Carol,viewerThe JSON version repeats id, name, and role three times each, plus six sets of braces and nine pairs of quotes around the keys. The TOON version declares the schema once and writes pure data rows. According to the official TOON benchmarks, the flat-only datasets (uniform tables like this) use 58.7% fewer tokens than pretty-printed JSON in the current v4.1 run.
Rule 5: Delimiters (Comma, Tab, Pipe)
TOON supports three delimiters for tabular rows and inline primitive arrays. Each array header declares its delimiter inside the brackets, so a decoder always knows how to split the rows. The JavaScript encoder takes a single delimiter option and applies it to every array in the document.
| Delimiter | Character | Header form | When to use |
|---|---|---|---|
| Comma | , | rows[2]{a,b}: | Default; best for most data |
| Tab | \t | rows[2<TAB>]{a<TAB>b}: | Values frequently contain commas |
| Pipe | | | rows[2|]{a|b}: | Readable middle ground; values contain commas but not pipes |
When a value contains the active delimiter, wrap it in double quotes. Switching the delimiter to tab or pipe eliminates most quoting for text-heavy datasets. Note the difference between two rules that are easy to confuse: tabs are forbidden for indentation, but tab is a valid delimiter.
Pipe-delimited example
orders[2|]{id|note}:
101|Ships Monday, weather permitting
102|Back-ordered, arrives FridayBoth notes contain commas. With the default comma delimiter the encoder would emit 101,"Ships Monday, weather permitting". Declaring pipe in the header ([2|] and {id|note}) means neither value needs quoting.
Rule 6: Nesting via Indentation
TOON uses significant whitespace (2 spaces per level; tabs are forbidden) to express hierarchy. A key followed by a colon and no inline value means the next indented block is its value. This works to arbitrary depth.
JSON
{
"user": {
"name": "Alice",
"address": {
"city": "Berlin",
"zip": "10115"
}
}
}TOON
user:
name: Alice
address:
city: Berlin
zip: "10115"Every level of nesting removes one pair of braces from the JSON representation. The zip code stays quoted because it is a string that looks like a number. You can place a table block inside a nested object at any depth; just keep indentation consistent. Under spec v4, a jump of more than one indentation level is a strict-mode decode error.
Rule 7: Numbers, Booleans, and Null
Scalar primitives use the same literals as JSON. Booleans are lowercase true and false, and null is null. Numbers are written in canonical form: 1.0 becomes 1, 1e6 becomes 1000000, and -0 becomes 0. Under spec v4, forms such as .5, +5, 0x10, 1_000, NaN, and Infinity are not numbers and decode as strings.
JSON
{
"count": 42,
"ratio": 0.875,
"large": 6.022e23,
"active": true,
"deleted": false,
"parent": null,
"whole": 1.0
}TOON
count: 42
ratio: 0.875
large: 6.022e+23
active: true
deleted: false
parent: null
whole: 1The token savings here come from removing the surrounding object braces and key quotes. The primitive values themselves are nearly token-identical to JSON.
Rule 8: Edge Cases
Values Containing the Delimiter
A table row value that contains the active delimiter must be quoted. The parser treats the first unquoted delimiter as a column separator.
products[2]{id,description}:
1,"Red, White & Blue widget"
2,Plain widgetEmpty Arrays
An empty array is always written as key: [] (or [] at the root), whether it would have held primitives or objects. The older key[0]: and users[0]{id,name}: forms are legacy: since spec 3.1 encoders must not emit them, though decoders still accept them.
tags: []
users: []Mixed or Non-Uniform Arrays
When array items do not share the same set of keys, or when they are not all objects, TOON falls back to list style. The header keeps the length (items[2]:), each item is a dash-prefixed entry, and the remaining fields of an object item are indented beneath it.
JSON
{
"items": [
{"type": "text", "value": "hello"},
{"type": "image", "url": "img.png", "width": 800}
]
}TOON (list fallback — fields differ between items)
items[2]:
- type: text
value: hello
- type: image
url: img.png
width: 800This is still more compact than JSON, but it does not get the full table-style savings. For best results, normalize your data to uniform schemas before converting — see TOON best practices for guidance.
Comments
Comments were added in spec v4.0 and are full-line only: a line whose first non-space character is # is ignored by the decoder. There is no inline or trailing comment form. Encoders never emit comments, so they are only for hand-written TOON, such as annotating a prompt.
# Retrieved from cache at 2026-06-03T09:00Z
config:
timeout: 5000
retries: 3This decodes to {"config":{"timeout":5000,"retries":3}}. Writing timeout: 5000 # milliseconds instead does not create a comment: the 4.1.1 decoder returns the string "5000 # milliseconds". Pre-v4 decoders do not understand comments at all, so upgrade decoders before you rely on them.
Rule 9: Keyed Tables and Nested Field Groups (Spec v4)
Spec 4.0 added two tabular forms that keep more real-world data in compact rows. Both decode with any v4 decoder; older decoders reject them.
Keyed Tables: Objects of Same-Shape Objects
When an object's values are all objects with the same primitive fields (a map keyed by ID or name), the encoder emits a keyed table. The colon inside the brackets ([3:]) marks it, and each row starts with the map key.
JSON
{
"flags": {
"darkMode": {"enabled": true, "rollout": 50},
"newCheckout": {"enabled": false, "rollout": 0},
"betaSearch": {"enabled": true, "rollout": 10}
}
}TOON
flags[3:]{enabled,rollout}:
darkMode: true,50
newCheckout: false,0
betaSearch: true,10Nested Field Groups: Uniform Nested Objects in Rows
When every row contains the same nested object, the header names its fields in a nested brace group and the row cells are flattened in order. Row cells are still primitives only.
JSON
{
"orders": [
{"id": 1, "customer": {"name": "Ada", "country": "UK"}, "total": 49.99},
{"id": 2, "customer": {"name": "Bob", "country": "US"}, "total": 199.0}
]
}TOON
orders[2]{id,customer{name,country},total}:
1,Ada,UK,49.99
2,Bob,US,199In the current benchmark, the feature-flags dataset (keyed tables) uses 54.6% fewer tokens than JSON and the contacts dataset (nested field groups) uses 66.5% fewer, per the token-efficiency results. Note that v4 also removed key folding (a.b.c: 1 dotted paths); the keyFolding option is ignored in 4.1.1.
Complete Example: JSON vs. TOON Side by Side
The following example combines objects, a table array, nesting, and scalars — the full range of TOON constructs in a single document.
JSON (361 characters pretty-printed)
{
"service": "recommendations",
"version": 2,
"active": true,
"config": {
"max_results": 10,
"threshold": 0.75
},
"users": [
{"id": 1, "name": "Alice", "tier": "pro"},
{"id": 2, "name": "Bob", "tier": "free"},
{"id": 3, "name": "Carol", "tier": "pro"}
]
}TOON (157 characters)
service: recommendations
version: 2
active: true
config:
max_results: 10
threshold: 0.75
users[3]{id,name,tier}:
1,Alice,pro
2,Bob,free
3,Carol,proIn the current official benchmark (v4.1, 5,856 LLM calls across four models), TOON used 42.6% fewer tokens than JSON and scored 72.2% retrieval accuracy versus JSON's 71.4%. The confidence intervals overlap, so treat accuracy as roughly equal and the token savings as the real gain. Full methodology and per-dataset breakdowns are at toonformat.dev/guide/benchmarks.
Quick Reference: When to Use Each Construct
| Your data shape | Use this TOON construct |
|---|---|
| Single object with scalar fields | Key-value block, unquoted bare words |
| Array of primitives | Inline (key[N]: a,b,c) |
| Array of mixed or non-uniform items | List style (key[N]: + - item) |
| Array of objects sharing identical keys | Table block (key[N]{f1,f2}: + rows) |
| Object whose values share the same fields | Keyed table (key[N:]{f1,f2}:) |
| Rows with a uniform nested object | Nested field group (key[N]{id,sub{a,b}}:) |
| Empty array | key: [] |
| Values contain commas | Quote the value, or switch delimiter to tab/pipe |
| Deep hierarchy | Indented nesting, 2 spaces per level (or compact JSON if the data is deep and irregular) |
| Value is ambiguous (looks like boolean/number) | Wrap in double quotes |
| Empty string | "" (always quoted) |
For the complete grammar and parser implementation notes, see the TOON specification. For interactive conversion, use /docs/toon or paste your JSON directly into the converter.
Frequently Asked Questions
How do you write an array of objects in TOON?
Use the tabular header: write the key, the item count in square brackets, and the field names in curly braces, followed by a colon. Each indented line below is one row of delimiter-separated values in field order, with no spaces after the commas. Example: users[2]{id,name}: then rows 1,Alice and 2,Bob.
When do TOON strings need quotes?
Quotes are required when a string is empty, has leading or trailing whitespace, equals true, false, or null, looks like a number ("42", "05"), contains a colon, double quote, backslash, bracket, brace, or the active delimiter, starts with - or #, or is exactly -. Everything else stays bare.
What delimiters does TOON support?
TOON supports three delimiters for tabular rows and inline primitive arrays: comma (the default, header users[2]{id,name}:), tab (header users[2<TAB>]{id<TAB>name}:), and pipe (header users[2|]{id|name}:). Each array header declares its own delimiter. Tabs are forbidden for indentation but valid as a delimiter.
How does TOON handle nesting?
TOON uses significant indentation (2 spaces per level by default, tabs forbidden) to express nesting. A key followed by a colon and no inline value signals that the next indented block is the value. Since spec v4, uniform nested objects inside array rows stay tabular through nested field groups such as orders[2]{id,customer{name,country},total}:.
Does TOON support comments?
Since spec v4.0, TOON supports full-line comments only: a line whose first non-space character is #. There is no inline or trailing comment form, so timeout: 5000 # ms decodes as the string "5000 # ms". Encoders never emit comments, and strings that start with # are quoted.
Recommended Reading
Choosing TOON Delimiters: Comma, Tab, or Pipe?
TOON delimiters: comma, tab, or pipe. How each affects tokenization, quoting, and readability, and how to pick the right one for your data and model.
TOON Format Specification: Grammar and Syntax Guide
Discover the official TOON (Token-Oriented Object Notation) specification, ABNF grammar, data types, and key syntax rules of this modern serialization format.
TOON v4: Keyed Tables, Nested Field Groups and Comments
TOON spec 4.1 adds keyed tables for maps of objects, nested field groups and full-line comments, and drops key folding. What changed and why it matters.