Excel to Markdown
How to convert an Excel table to Markdown
rows.page turns up to 1,000 rows of pasted Excel cells, or of a CSV up to 1.5 GB, into a GitHub Flavored Markdown table, free with no signup. Copy the cells, click Paste data, then choose Export > Markdown; pipes are escaped. .xlsx files don't open, so paste the cells or save a CSV first.
No file handy? Open CO2 emissions by country and year (50k rows, 14 MB).
Copy Excel cells to Markdown on rows.page
- Select the cells in Excel, header row included, and copy them.
- Open rows.page, click Paste data and paste. Excel copies cells as tab-separated text, so they open as a table.
- Open the Export menu and choose Markdown. rows.page downloads the table as a Markdown file, ready to paste into a
README.md, an issue or your docs.
rows.page can't read a workbook: a dropped .xlsx or .xls file won't open as a sheet, and pushing one returns "This looks like an Excel or zip file. Export it as CSV and push again." To work from a file, save the sheet as CSV UTF-8 (Comma delimited) and drop it on the page. Everything runs in your browser with DuckDB-WASM, and nothing is uploaded unless you press Share.
| Method | Input | Rows in the table | Escapes pipes |
|---|---|---|---|
| rows.page Paste data | Copied cells | First 1,000 | Yes |
| rows.page file | CSV or TSV saved from Excel, up to 1.5 GB | First 1,000 | Yes |
pandas to_markdown() | .xlsx or CSV, needs tabulate | All | No |
What the Markdown export writes
- A header row and a plain
| --- |delimiter row, with no column alignment set. - The first 1,000 rows of the current view, without hidden columns. The Export menu marks Markdown "first 1,000", but the file itself doesn't say when rows were cut.
- Pipes inside cells become
\|and backslashes become\\, so the columns stay in place. - Line breaks inside a cell become a single space, because a Markdown row has to stay on one line.
- Empty cells stay empty, and list columns are written as JSON text.
| item | note | price |
| --- | --- | --- |
| Pipe \| tube | line one line two | 25 |
| Bracket | | 10 |Trim and reshape the table before exporting
The export follows your current view, so cut the table down first:
- Hide columns from the column header menu or the Columns panel.
- Filter rows with
country:NL,price:>20,name:*(has a value) or-name:*(empty), or click a histogram bar or a top value in the Columns panel. - Click a column name to sort and shift-click to add a second sort, so the rows you need land inside the first 1,000.
To rename headers or reorder columns, type SQL into the same box. The table is called data, and the column names in your result become the Markdown header:
SELECT item AS "Item", price AS "Price"
FROM data
WHERE price > 20
ORDER BY price DESCNaming the columns also leaves out the hidden __rid row-number column. To drop duplicate rows, use SELECT DISTINCT * EXCLUDE (__rid) FROM data.
Set column alignment after exporting
GitHub Flavored Markdown sets alignment with colons in the delimiter row. rows.page doesn't add them, so edit that row after export:
:---aligns the column left.:---:centers it.---:aligns it right, which suits numbers.
| Item | Note | Price |
| :--- | :---: | ---: |
| Pipe \| tube | a \| b | 25 |
| Bracket | | 10 |Convert Excel to Markdown in Python with pandas
For more than 1,000 rows, or to script the conversion, use pandas. DataFrame.to_markdown() needs the tabulate package, doesn't escape pipes, prints empty cells as nan and lets a line break split the row, so clean the cells first:
pip install pandas openpyxl tabulateimport pandas as pd
df = pd.read_excel('sheet.xlsx').fillna('')
df = df.replace({r'\|': r'\\|', r'\n': ' '}, regex=True)
print(df.to_markdown(index=False))tabulate right-aligns number columns (---:) and left-aligns text (:---). read_excel also turns text such as 02134 into a number unless you pass dtype={'zip': str}.
What Markdown tables can't hold
- Merged cells: Markdown has no column or row spans. Excel keeps a merged range's value in its upper-left cell only, so the other cells arrive empty.
- Formulas and formatting: copied cells carry the values Excel displays, not formulas, colors or bold text.
- Line breaks: a cell can't span lines, so rows.page joins them with a space. For another separator, run
SELECT * EXCLUDE (__rid) REPLACE (replace(note, chr(10), ' / ') AS note) FROM databefore exporting. - Size: the export stops at 1,000 rows. For a bigger table, press Share and link to the hosted copy instead.
rows.page limits and prices
| What | Limit or price |
|---|---|
| Open a file in your browser | Files up to 1.5 GB, $0, no account, nothing uploaded |
| Share a link without an account | $0, files up to 100 MB, link deleted 7 days after creation |
| Free account | $0, 10 datasets kept, last 3 versions |
| Pro | $12/month, 100 datasets, files up to 500 MB, private links |
| Max | $39/month, 1,000 datasets, files up to 2 GB, private links |
Questions
Does rows.page upload my Excel data?
No. Pasted cells and opened files are read inside your browser with DuckDB-WASM. Nothing is uploaded unless you press Share.
Why won't rows.page open my .xlsx file?
rows.page doesn't read .xlsx or .xls files, and pushing one returns "This looks like an Excel or zip file. Export it as CSV and push again." Copy the cells and click Paste data, or save the sheet as CSV UTF-8 first.
How many rows can the Markdown export hold?
The first 1,000 rows of the current view. For all rows, export CSV, Parquet, JSON or JSONL instead: a filtered view exports every matching row, and a SQL result up to 1,000,000.
How do I share a table that's too big for Markdown?
Press Share ("Upload and get a link") to get a https://rows.page/<id> link. On the hosted page, Copy link (Copy link to this view on mobile) shares the current filter, sort and visible columns. One push is capped at 100 MB, and the link expires after 7 days unless you press Keep it on the dataset page; a free account keeps up to 10 datasets.
Why won't my CSV with euro signs open in rows.page?
A CSV that isn't UTF-8 is read as Latin-1, but a euro sign, curly quotes or long dashes saved in a Windows encoding stop it from opening ("Couldn't open this data"). Save it again as CSV UTF-8 (Comma delimited), or copy the cells and use Paste data, which keeps those characters.