CSV to reStructuredText Converter

A CSV to reStructuredText converter turns a spreadsheet into a table Sphinx and Read the Docs will build. This one offers all four table syntaxes, draws the borders to the right width even when your text is Japanese, and refuses to write a simple table with a blank first column, which is how docutils silently merges two rows into one. Nothing is uploaded.

Need to trim or reorder columns first? Open the app

The blank cell that eats a row

reStructuredText's simple table looks like the obvious choice. It is much easier to read as source than the grid syntax, it is what the docutils documentation shows first, and it takes a fraction of the characters:

========  ==========  =======
sku       name        price
========  ==========  =======
00412     Bracket       4.25
01730     Bushing       2.15
========  ==========  =======

It also has a rule nobody reads until it bites: the first column cannot be empty in any row. docutils uses the left edge to find where a row starts, so a row whose first cell is blank is read as a continuation of the row above. The two are merged. Your table now has one fewer row than your data, Sphinx builds without an error, and the missing line is discovered by whoever reads the published page.

A spreadsheet with a blank first cell is completely ordinary. A grouped report leaves the group name blank on all but the first row of the group, which is the exact shape that breaks this.

So the check happens before anything is written. If you ask for a simple table and the data has a blank first column, you get the grid syntax and a warning saying what would have happened. Two columns or fewer also falls back, since a simple table needs a boundary to find.

Four syntaxes, and when each one wins

Grid draws a full border around every cell. It is verbose and it handles anything, including blank cells anywhere:

+-------+----------+-------+
| sku   | name     | price |
+=======+==========+=======+
| 00412 | Bracket  | 4.25  |
+-------+----------+-------+

The row of equals signs under the header is not decoration. It is what tells docutils the first row is a header; with hyphens there instead you get no header styling and no <th> elements. Hand-written grid tables get this wrong constantly, and it is written for you here.

csv-table hands the data to Sphinx as CSV and lets it build the table:

.. csv-table:: Component prices
   :header: "sku", "name", "price"
   :widths: auto, auto, auto

   "00412", "Bracket", "4.25"
   "01730", "Bushing", "2.15"

This is the one to use for anything long. Five hundred rows are five hundred short lines rather than fifteen hundred lines of border art, and adding a row does not require redrawing anything. Every value is quoted, so a comma in a cell is safe.

list-table writes each cell as a bullet. It is the most verbose per row and produces the cleanest diffs, because changing one cell touches exactly one line rather than shifting a border. On a docs repo where tables get reviewed in pull requests, that is worth a lot.

Widths, CJK and line breaks

A grid table only works if every line is exactly the same number of characters wide. Get one line wrong and docutils reports a malformed table, or worse, silently reads the columns at the wrong boundaries.

That is easy until the data is not ASCII. A Japanese character is one JavaScript character and takes two terminal columns. An emoji is two JavaScript characters and takes two columns. A combining accent is one character and takes none. Measuring with .length gets all three wrong, which is why most generated RST tables with CJK content have a ragged right edge and half of them fail to build.

Widths here are measured in display columns, with the East Asian Wide and Fullwidth ranges, the emoji blocks and the combining marks all accounted for. A table of Japanese city names comes out with a straight edge.

A line break inside a cell becomes a space, and the count is reported. Grid tables can hold multi-line cells in principle, but combining that with automatic width calculation produces source nobody can read or edit. If a cell genuinely needs a paragraph, list-table is the syntax that takes it comfortably, and the fallback there is a note rather than a silent flattening.

Stub columns are offered on the two directive syntaxes. They mark the leftmost columns as row headers, which makes docutils emit <th scope="row"> and lets a screen reader announce the row's label before each cell. Most themes also bold them.

Questions

Why can a simple table not have an empty first column?

Because docutils uses the left edge to find where a row begins. A blank first cell reads as a continuation of the row above, so the two rows are silently merged and your table quietly loses a line with no warning from Sphinx. This converter checks for it before writing anything and switches to the grid syntax instead, telling you why. It is the single most common way an RST table goes wrong.

Which of the four syntaxes should I use?

Grid is the safe default: it handles any content including blank cells and multi-line text. Simple is far easier to read as source and has the blank-first-column restriction. csv-table is best for long tables because Sphinx parses the CSV itself, so 500 rows are 500 short lines instead of 1,500 lines of border art. list-table produces the friendliest diffs, since changing one cell touches one line.

What is the difference between the = and - rules in a grid table?

A row of equals signs marks the boundary between the header and the body. A row of hyphens is an ordinary row separator. If every rule is hyphens, docutils treats the whole table as body rows and you get no header styling and no th elements in the output. This writes the equals rule under the header automatically, which is the other common thing hand-written RST tables get wrong.

Are the column widths correct for CJK text?

Yes. Grid table borders only line up if every line is exactly the same width, and a CJK character or an emoji occupies two terminal columns while being one or two JavaScript characters. Widths are measured in display columns rather than string length, so a table containing Japanese or Chinese text still has a straight right edge. Measuring with length is why most generated RST tables look ragged.

What are stub columns?

Stub columns are row headers: the leftmost columns that identify the row rather than carry a value. Marking them makes docutils emit th elements with a row scope, which is what lets a screen reader announce the row's label before each cell. It also usually gets bold styling in the HTML theme. The option applies to the csv-table and list-table directives.

What happens to a line break inside a cell?

It becomes a space, and the count is reported. A grid table can technically hold multi-line cells, but only when the borders are drawn around them, and mixing that with automatic width calculation produces tables that are correct and unreadable as source. If you need a genuine paragraph inside a cell, the list-table directive is the syntax that handles it comfortably.

Is anything uploaded?

No. The conversion runs in your browser tab, nothing is sent to a server, nothing is kept between visits and there is no row cap. Paste the result into your .rst file and Sphinx or Read the Docs will build it as it stands.

Convert your CSV to reStructuredText

No sign-up, no upload, no row cap. Four syntaxes, correct header rules, and the row-eating trap caught before it happens.

Back to the converter