T tomlkit·org
Inspect Formatter Validator Convert TOMLJSON JSONTOML TOMLYAML YAMLTOML INITOML TOMLINI .envTOML TOML.env TOMLTS Transform Sort keys Flatten Minify Compare Diff Merge

TOML Sorter

updated 8 June 2026

Sort every table's keys alphabetically and re-emit canonical TOML. Use it to make diffs reproducible, to find a key quickly in a long file, or to enforce a house style. Comments are dropped.

canonical TOML 1.0 output

What this tool does

It parses your TOML, reorders the keys inside each table alphabetically, and re-emits the document. The only thing it decides is key order — A before B before C, in every table by default. There are exactly three knobs, and all three are about ordering: the direction of the sort, how deep it reaches, and whether case is folded. There is no quote-style picker, no inline-vs-block picker, no array-spacing or trailing-comma control. This is a sorter, not a restyler.

The intent it closes: "I want this config's keys in a predictable order so my diffs stop churning and I can find a setting by eye." The usual destinations are a Cargo.toml or pyproject.toml in a pull request, a long service config you grep through daily, or a pre-commit hook that wants byte-stable output no matter who edited last.

If you actually want to restyle TOML — choose basic vs literal quotes, force tables inline or block, compact arrays, add trailing commas, wrap long lines — that's the TOML Formatter's job, and it has a sort option of its own. Reach here when reordering keys is the only change you want to introduce.

When you'd reach for it

  • Stop diff churn in a PR. Sort both the old and the new file so the diff shows real edits, not the order two people happened to type keys in. Keep Order on Alphabetical, Scope on Recursive.
  • Find a key in a 500-line config by eye. Alphabetical order turns a linear scan into a binary search.
  • Enforce a house style from a pre-commit hook. Run sort on every config so the repo stays in one canonical order, regardless of editor.
  • Keep scalars above sub-tables in each section. Pick Alpha, tables last so name and version sit above [dependencies] in every section instead of being interleaved.
  • Reorder only the top-level sections. Set Scope to Top-level only to sort the outermost keys and leave the contents of each table exactly as written.
  • Group keys that differ only by case. Switch Case to Case-insensitive so Name and name land next to each other instead of all-uppercase keys clustering first.

How sorting works

One click of Sort runs three stages.

1. Parse the input

The full TOML 1.0 grammar is parsed into a value tree — dotted keys collapse into nested tables, dates become real datetime values, integers keep their hex/octal/binary intent. If the input isn't valid TOML, the status bar shows the parser error and nothing is written; the sorter and the validator share the same parser. Comments live only in the source text, not in the tree, so they don't survive this stage.

2. Reorder the keys

This is the only transform. Under Recursive scope, every table at every depth has its keys sorted, including nested tables and the bodies of arrays-of-tables ([[servers]]). Under Top-level only scope, just the outermost keys are sorted and everything inside each table is left in its original order. Array values are never touched — ports = [8001, 8000] stays in that order; only object/table keys move.

3. Re-emit canonical TOML

The reordered tree is written back out with the emitter's defaults and the sort step's result locked in — internally the emitter is told sort: "preserve", so it prints keys in exactly the order step 2 produced and never re-sorts them. Because re-emission goes through the canonical writer, spacing around = and string quoting come out normalized as a side effect. That's unavoidable in any parse-and-re-emit flow; it is not a feature you can configure here, and unlike the formatter there are no controls to change it.

Options reference

Order

Alphabetical (the default) sorts every table's keys A–Z. Alpha, tables last sorts A–Z too, but first pushes any key whose value is a table — or an array of tables, like [[servers]] — below the scalar keys. Use it to keep simple settings (name, version, edition) clustered at the top of each section and the nested blocks beneath them.

Scope

Recursive (every table) sorts keys at every depth — top-level keys, nested tables, and the contents of each array-of-tables entry. Top-level only sorts just the outermost keys and leaves the inside of every table untouched, which is what you want when the section order is the only thing you care about and the contents are already arranged deliberately.

Case

Case-sensitive (the default) sorts by raw code point, so every uppercase letter sorts before every lowercase one — Z comes before a. Case-insensitive lowercases keys for the comparison only, so Name and name sort together by their letters rather than splitting across the case boundary. The keys themselves are never rewritten; only the comparison changes.

Output

  • Same document, keys reordered. The value tree is semantically identical to your input — only the order of keys within tables changes (plus the canonical re-emission side effects below).
  • Encoding: UTF-8, no BOM. Line endings are LF (\n), with a trailing newline on a non-empty document.
  • Spacing and quoting are normalized. A side effect of re-emission, not a configurable choice: one space on each side of =, basic double-quoted strings, spaced arrays, a blank line before each section header. To control any of that, use the formatter instead.
  • Comments: not emitted. The parser doesn't carry them into the tree, so the writer has nothing to print. Keep your source file if the comments matter.
  • Array values keep their order. Sorting reorders keys, never the elements of an array.
  • Download: sorted.toml with a text/plain content type.

Example

Input:

[package]
version = "1.4.2"
name = "tomlkit"
edition = "2024"
authors = ["S."]

[dependencies]
toml = "0.8"
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
anyhow = "1"

Output (defaults — Alphabetical, Recursive, Case-sensitive):

[dependencies]
anyhow = "1"
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
toml = "0.8"

[package]
authors = ["S."]
edition = "2024"
name = "tomlkit"
version = "1.4.2"

Notice [dependencies] now sorts above [package] because d precedes p — section headers sort alongside everything else. The authors = ["S."] array keeps its single element; only keys moved. If you'd rather [package] stayed before its sub-tables, switch Order to Alpha, tables last; if you'd rather the section order didn't change at all, switch Scope to Top-level only and the keys inside each table still sort while the sections keep their place.

Recipes by intent

Make a config diff-stable for a pull request

Leave every option at its default: Order Alphabetical, Scope Recursive, Case Case-sensitive. Sort the file on both sides of a change and the diff collapses to the lines that actually changed.

Keep scalars above sub-tables in every section

Set Order to Alpha, tables last. Keys like name and version cluster at the top of each table; [dependencies] and other nested blocks drop below them — readable for a big Cargo.toml.

Reorder sections without touching their contents

Set Scope to Top-level only. The outermost keys sort alphabetically while the inside of each table stays exactly as you arranged it — handy when a section's key order is meaningful but the section order isn't.

Sort mixed-case keys by their letters

Set Case to Case-insensitive. Instead of all-uppercase keys clustering ahead of lowercase ones, API, api_key, and App sort together by spelling. The keys are compared folded but written back exactly as you typed them.

Limits and performance

  • Memory-bound. The parser builds the full value tree before re-emission, so input, tree, and output all sit in memory at once. The rule of thumb for these in-browser tools is roughly 500 MB on desktop and 100 MB on mobile. Cargo.lock-sized configs sort instantly; multi-megabyte TOML takes a beat.
  • The textarea is the slow bit on huge files. Past tens of megabytes (around 50–100 MB), painting the result back into the right pane lags even after the sort itself finishes — use Download .toml rather than Copy.
  • Comments are not preserved. If your file is comment-heavy, sort a copy and keep the original as the source of truth.
  • For multi-gigabyte jobs, use a CLI. A browser tab is the wrong place for files that large; a streaming command-line sorter is the right tool.

Errors and how to fix them

Cannot sort: root is not a table.

You picked ScopeTop-level only but the parsed input isn't a top-level table — for example a bare value or fragment rather than a key-value document. Top-level sorting needs an object of keys to reorder. Use Recursive scope, or make sure the document's root is a normal TOML table.

"Parse error" / status bar goes red on click

The input isn't valid TOML 1.0. Common causes: unclosed quotes, an array that opens with [ but never closes, a missing = between key and value, or a duplicate key in the same table. The TOML Validator points at the exact line and column.

My # comments disappeared

Expected. The parser doesn't carry comments into the value tree, so the writer has nothing to re-emit. Keep your original file as the source of truth and sort a copy for diffs or shipping.

The spacing and quotes changed, not just the order

That's the canonical re-emission side effect: parse-and-re-emit always normalizes spacing around = and string quoting. The sorter has no controls to change this. If you need to choose the style — literal quotes, compact arrays, inline tables — run the formatter, which exposes those options.

Uppercase keys all jumped to the top

Case-sensitive ordering sorts by code point, so every uppercase letter sorts before every lowercase one. Switch Case to Case-insensitive to sort by the letters regardless of case.

FAQ

How is this different from the TOML Formatter's sort option?

The formatter is a full restyler — it lets you pick quote style, inline-vs-block tables, array spacing, trailing commas, and line wrapping, with sorting as one option among many. This tool does only the sort: its three controls are all about key order, and it never asks you to choose a style. Use it when reordering keys is the single change you want; use the formatter when you want to re-style the whole document.

Will it move [package] before [dependencies]?

Under Recursive scope, yes — top-level section headers sort alongside everything else, so [dependencies] lands before [package]. If you want to keep a deliberate section order, use Top-level only scope (the contents still sort) or Alpha, tables last to at least keep scalars above sub-tables.

Does it reorder array values too?

No. Sorting only reorders the keys inside tables. The elements of an array — ports = [8001, 8000] — keep their original order, and so do the entries of an array-of-tables (their contents sort, but their sequence doesn't move).

Does it handle the full TOML 1.0 spec?

Yes — dotted keys, every date variant, hex/octal/binary integers, and array-of-table syntax ([[…]]). Anything the spec disallows (mixed array types, duplicate keys) errors out at parse time before any sorting happens.

Is my data uploaded?

Never. The parser and writer run entirely in your browser. You can disconnect from the network before clicking Sort and it still works — safe for configs holding secrets or API keys.