TOML to JSON Converter

Paste a pyproject.toml, a Cargo.toml, or a hand-edited config, and read back a JSON object with every table, dotted key, and [[array entry]] where a parser expects it. Duplicate keys and integers past safe JavaScript precision get called out instead of quietly rounded.

Load a file

TOML

0 B

JSON

0 B

Waiting for TOML.

Tables

0

[[Array]] entries

0

Keys

0

Dates

0

Big integers

0

Duplicate keys

0

    A config format that looks friendlier than it is

    TOML reads like plain key = value lines until it does not. A file stays readable right up to the point where a nested table, a dotted key, or a repeated [[header]] shows up, and that is exactly where a hand-rolled regex parser starts guessing instead of parsing. This page runs a real recursive parser against the text you paste, the kind that tracks which table it is currently inside rather than reading line by line, so the JSON on the right matches what a TOML library in Python or Rust would hand back.

    Nothing leaves your browser. The parsing happens in a script tag on this page, so a config file with internal hostnames or a database password in it never reaches a server.

    Tables become objects, dotted keys become paths

    A [table] header opens an object and every key = value line after it, up to the next header, becomes a property of that object. Nesting follows the dots in the header:

    [tool.ruff]line-length = 100
    target-version = "py311"

    becomes:

    { "tool": { "ruff": { "line-length": 100, "target-version": "py311" } } }

    A key can carry its own dots without a header at all. owner.name = "Wajahat Qasim" creates an owner object with a name field inside it, whichever table it appears in. Cargo dependency blocks lean on this by mixing inline tables and plain keys in the same section, and the converter treats both spellings the same way once it reaches the leaf.

    Two brackets means a list, not a nested table

    [[fruits]] is not a typo for [fruits]. Every time that header appears, it pushes a new object onto an array named fruits, and any plain [fruits.something] header that follows attaches to whichever entry was pushed most recently, not to the first one. A products catalog with two [[catalog.products]] blocks and a [catalog.products.warehouse] table after each one ends up with two separate warehouse objects, one per product, in the order the file listed them. Netlify's redirect rules and pyproject's tool sections both rely on exactly this to represent a list of similar blocks without inventing an array-of-strings workaround.

    TOMLBecomes in JSON
    [section]An object under the key section
    [[section]]An object appended to an array under section
    a.b = 1{ "a": { "b": 1 } }
    x = { y = 1 }{ "x": { "y": 1 } }, written inline in the source
    2026-04-18The JSON string "2026-04-18"

    Where this parser disagrees with the spec. It is a browser tool, not a certified TOML implementation, and it takes a few shortcuts on purpose:

    • An inline table with a line break inside the braces parses here. The official grammar forbids that break, but pasted configs have it anyway, and rejecting the file over one newline helps nobody.
    • Duplicate key and table redefinition checks cover the common mistakes, an accidental repeated key = value line or a repeated [header]. Some of the rarer redefinition patterns in the formal ABNF are not enforced.
    • A signed hex, octal, or binary literal like -0x10 is accepted, even though the spec reserves a leading sign for decimal integers only.
    • The space-separated datetime variant from RFC 3339 (1979-05-27 07:32:00) is not recognised as a date. Use a capital T, which is what every sample on this page does.

    Run the file through Taplo or Python's tomllib before it goes anywhere that checks for strict compliance, such as a Rust build or a linter in CI.

    Numbers and dates that do not survive the trip unchanged

    A TOML date, whether it carries a time zone or not, has no equivalent in JSON. It comes out as a plain string, so released = 2026-04-18 becomes "released": "2026-04-18". That is expected, not a bug, and most JSON consumers parse the string back into a date on their own side.

    Integers are the value that people notice too late. JavaScript numbers are IEEE-754 doubles, so anything past 253, roughly 16 decimal digits, cannot be represented exactly. A Snowflake ID or an order number that big gets rounded to the nearest value a double can hold the moment a naive converter reads it. This tool checks every integer against that ceiling first. Past it, the digits go into the JSON as a raw, unquoted number rather than a rounded one, since JSON's own grammar allows numeric literals of any length. That keeps the value exact in the text you copy, though a reader who then runs it through JSON.parse in JavaScript will round it right back, since the round-trip problem lives on the reading side, not the writing side.

    TOML also has inf, -inf, and nan as float literals. JSON has none of the three. JSON.stringify in a browser console quietly turns all three into null, which erases the fact that a value was ever there. This converter writes them as the strings "inf", "-inf", and "nan" instead, so the signal survives even though the type changes from number to string.

    What happens when a key is set twice?

    The TOML specification treats a repeated key inside the same table as an error, full stop, not a "last one wins" situation. That is stricter than JSON, where a duplicate key is technically undefined behavior and most parsers just keep the last one. The checkbox above the editors controls which rule this page follows. Checked, a second host = "..." line under the same [server] table stops the conversion with a line number. Unchecked, the tool falls back to overwrite semantics and reports how many duplicates it saw, which is closer to how a lot of production JSON tooling already behaves and is useful when you inherited a config file you cannot fix yet.

    Files people actually paste here

    pyproject.toml, when a build script needs the dependency list and version as JSON for a release pipeline. Cargo.toml, when auditing a Rust project's dependency versions and feature flags outside of cargo itself. netlify.toml, to check that a redirect rule ended up in the right array position, since order matters for the first matching rule to win. Prompt and shell tool configs, such as Starship's starship.toml, where a quoted key like "$schema" needs to survive the trip intact. And catalogs or fixture data that someone chose to write in TOML for the comments, then needs back in JSON for a test suite.

    Where this stops

    The parser handles files up to a few megabytes without the live re-parsing on every keystroke feeling slow. Past that, paste the file in stages or use a command-line tool. It does not resolve include directives, because standard TOML has none, extensions that add one are outside scope here. And it will not tell you whether the resulting JSON matches a schema your application expects. That is a separate check, and the JSON to JSON Schema generator on this site is a reasonable next stop for it. Going the other direction, the JSON to TOML converter turns an object back into table headers and array-of-tables blocks with its own set of formatting choices.

    TOML to JSON questions

    Why does a duplicate key stop the conversion instead of just picking one value?

    The TOML specification defines a repeated key in the same table as invalid, not as overwrite behavior. Checking the box above the editors keeps that rule and shows the line number of the second occurrence. Unchecking it switches to overwrite semantics, where the later value wins and the report panel counts how many duplicates were found.

    What happens to a [[array of tables]] header like [[redirects]]?

    Each occurrence appends a new object to an array named redirects. A sub-table declared right after it, such as [redirects.headers], attaches to whichever entry was appended most recently, matching how tomllib and other TOML libraries resolve the same file.

    My dates turned into quoted strings. Is that a bug?

    No. JSON has no date type, so a TOML date, time, or date-time can only become a string once it crosses over. The report panel counts how many values were recognised as dates so you can spot-check them.

    A number came out much longer than I typed it. What happened?

    Nothing was invented. Integers past roughly 16 digits cannot be represented exactly by a JavaScript number, so this converter checks first and writes the exact digits as a raw JSON number instead of rounding. If your own code reads the JSON with JSON.parse, it will round the value the same way any JavaScript number does, so treat fields like that as strings downstream if the exact digits matter.

    Does the converter accept a multi-line inline table even though the spec forbids it?

    Yes, on purpose. A line break inside { } is invalid TOML by the letter of the grammar, but pasted configs sometimes have one anyway, and rejecting the whole file over a stray newline is not useful. Run the file through a strict validator such as Taplo before it goes into a system that enforces the formal grammar.

    Is anything uploaded when I paste a config file here?

    No. The parser and the JSON writer are plain JavaScript running in this page. Nothing is sent to a server, so credentials, internal hostnames, and API keys in the file stay on your machine.