7 min read

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.

By json2toon.co

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.

ConstructJSON formTOON formKey saving
Object{"k": "v"}k: vNo braces or quotes
Primitive array["a","b","c"]key[3]: a,b,cOne line, length declared
Array of objects (uniform)Array with repeated keyskey[N]{f1,f2}: + rowsKeys declared once; rows are CSV
Keyed table (v4)Object of same-shape objectsflags[3:]{enabled,rollout}: + name: v1,v2 rowsMap keys become row labels
Nested field group (v4)Uniform nested object in each roworders[2]{id,customer{name,country},total}:Nested objects stay tabular
Non-uniform arrayObjects with differing keyskey[N]: + - item linesNo braces or brackets
Empty array[]key: []Canonical since spec 3.1
Nested objectNested bracesIndented key-value blockNo braces at any depth
Unquoted string"active"active2 tokens saved per value
Quoted string"hello, world""hello, world"Quotes only when needed
Number4242Identical
Booleantrue / falsetrue / falseIdentical
NullnullnullIdentical
Comment (v4)Not supported# text on its own line onlyFull-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: true

Three 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), or null.
  • 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-sale

The [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,viewer

The 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.

DelimiterCharacterHeader formWhen to use
Comma,rows[2]{a,b}:Default; best for most data
Tab\trows[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 Friday

Both 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: 1

The 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 widget

Empty 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: 800

This 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: 3

This 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,10

Nested 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,199

In 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,pro

In 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 shapeUse this TOON construct
Single object with scalar fieldsKey-value block, unquoted bare words
Array of primitivesInline (key[N]: a,b,c)
Array of mixed or non-uniform itemsList style (key[N]: + - item)
Array of objects sharing identical keysTable block (key[N]{f1,f2}: + rows)
Object whose values share the same fieldsKeyed table (key[N:]{f1,f2}:)
Rows with a uniform nested objectNested field group (key[N]{id,sub{a,b}}:)
Empty arraykey: []
Values contain commasQuote the value, or switch delimiter to tab/pipe
Deep hierarchyIndented 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

TOONCheat SheetSpecificationSyntaxReferenceDeveloper Guide