Convert JSON to TOML without hand-typing every table header
Most JSON to TOML jobs start the same way. A build script, an API, or a code generator hands you a JSON object, and the file you need to ship is pyproject.toml, Cargo.toml, netlify.toml, or a Hugo site config. Typing the headers by hand goes wrong at the third level of nesting, and the first mistake a TOML parser reports is usually a table defined twice. This page does the walk for you. Paste the JSON, and the output pane fills with a document where every plain value sits above the sub-tables of its parent, which is the order TOML requires.
The conversion runs in your browser and nothing leaves the tab. The parser is the browser's own JSON.parse, so a stray trailing comma or an unquoted key stops the page with a line and column. Click "Go to the line" and the cursor lands on the problem in the JSON pane.
One nested object, three ways to write it
TOML gives you more than one spelling for a nested object, and the file you are targeting usually has a house style. Take this fragment:
{ "owner": { "name": "Wajahat Qasim", "role": "maintainer" } }With Table headers selected, the converter writes a block, which is how Cargo.toml spells [package] and how pyproject spells [project]:
[owner]name = "Wajahat Qasim"
role = "maintainer"Dotted keys keep the pairs at the parent level. Hugo configs and small [tool.*] sections read well this way, since a two-key object does not earn its own header:
owner.name = "Wajahat Qasim"
owner.role = "maintainer"Inline tables put the object on one line. Cargo dependency specs are the classic case, serde = { version = "1.0", features = ["derive"] }. The converter only inlines objects whose values are all plain scalars and whose key count stays under the limit you set. Anything deeper falls back to a header, because a TOML inline table has to fit on a single line and cannot be extended later in the file.
Whichever mode you pick, an object with sub-objects of its own always becomes a header. The dotted and inline forms are shortcuts for flat leaves, not a general replacement for tables.
Arrays of objects become [[double bracket]] tables
A JSON array where every element is an object maps to TOML's array of tables. Netlify redirects are the pattern most people recognise:
[[redirects]]from = "/blog/*"
to = "https://toolexe.com/articles/:splat"
status = 301
[[redirects]]from = "/*"
to = "/index.html"
status = 200Nesting inside those entries works the way the TOML spec describes. A sub-object under a [[fruits]] entry is written as [fruits.physical] and attaches to the most recent entry. A nested array of objects becomes [[fruits.varieties]]. The "Headers written" list on the right shows every header in the order the file uses them, with entry counts, so you check the shape before you scroll the output.
If an array mixes objects with strings or numbers, or one element is a nested array, the whole array is written inline with inline tables for the objects. TOML 1.0 allows mixed-type arrays. TOML 0.5 readers reject them, so an older parser in your toolchain is a reason to reshape the JSON first.
Switch "Arrays of objects" to the inline setting when the objects are tiny and there are many of them. A list of forty one-line { name = "...", weight = 10 } tables scans faster than forty [[menu.main]] blocks.
What TOML refuses to take from JSON
JSON is a superset of what TOML expresses in a few places, and the converter has to make a call each time. The "Worth checking" panel names every one of them for the document you pasted.
- null. TOML has no null. The default drops the key, which is what most readers treat as "unset" anyway. Choose the comment option when a reviewer needs to see the key was there, or the empty-string option when the schema demands the key exist. None of the three are lossless, so the panel lists the paths that were affected.
- A top-level array. A TOML document is a table, full stop. Paste
[{...}, {...}]and the page stops with an offer to wrap the array under anitemskey, which turns each object into a[[items]]entry. Rename the key afterwards. - Integers versus floats. JSON does not distinguish
3from3.0. JavaScript reads both as the number 3, so the TOML says3. That is a type change in TOML, and a strict reader expecting a float will complain. The panel counts how many values were written with a.0in the source. - Large integers. Anything past 15 or 16 digits gets rounded by the browser before the converter runs. Order IDs and snowflake IDs belong in quotes in the JSON if the digits matter.
- Keys outside A-Z, 0-9, dash and underscore. Spaces, dots, and unicode force a quoted key,
"site name" = "Toolexe". A dotted key inside a quoted key stays literal, so"owner.email"is a single key and not a nested one. - Empty objects.
{}has no header form, so the converter writeskey = {}as an inline table.
Strings, escapes, and the date guess
Plain strings go out in double quotes with the usual escapes for quotes, backslashes, tabs, and control characters. Two toggles change that for specific shapes:
- A string with backslashes and no single quote, a Windows path or a regex, is written as a literal string in single quotes, so
'C:\deploy\releases'stays readable instead of doubling every backslash. - A string with line breaks becomes a multi-line basic string inside
"""delimiters. The first newline after the opening delimiter is trimmed by the parser, which is why the output starts the text on the next line.
Date detection is the toggle to watch. A string matching RFC 3339, such as 2026-09-11T14:05:00Z, a bare date 2026-04-18, or a bare time 14:05:00, goes out unquoted, and a TOML reader returns a real datetime instead of text. That is usually what a config wants. Turn the toggle off when the field is a version label or a string ID that only looks like a date, because the type change follows the value into your application.
Files people paste here
pyproject.toml from a setup.py migration script or from pip inspect. Use table headers, since [project], [build-system], and [tool.ruff] are the shape every Python tool reads. Cargo.toml from a dependency audit, where inline tables keep the dependency block compact. netlify.toml and Hugo's hugo.toml, where arrays of tables carry redirects, headers, and menu items. And plain application config, where JSON was the storage format and TOML is what the ops team asked for because comments are allowed.
One thing the page cannot do is invent the comments. TOML's main advantage over JSON is a # line above a setting explaining why the value is what it is. Add those after you paste the output into the repo.
Where this page stops
The converter handles documents in the low megabytes comfortably. Past that, the live re-conversion on every keystroke starts to lag, and a command line tool is the better fit. It writes TOML 1.0, so mixed arrays and multi-line inline values assume a modern parser. Key order follows the JSON unless you sort, and the sort is plain code-point order, which puts uppercase before lowercase. Comments in the input are not supported because JSON has none. Blank lines between tables are fixed at one. If you need the reverse trip, the TOML to JSON converter parses TOML back into an object, and the TOML validator checks a file you edited by hand.
