Paste a CSV and read it as a table. Unlike a spreadsheet, it keeps every
cell as text, so the leading zeros on postal codes survive
and 1-2 stays 1-2 instead of
turning into a date. Copy for Email puts a
bordered table on the clipboard that pastes into Gmail,
Outlook, Google Docs or Word as a real table. Copy
HTML gives you plain <table>
markup for a web page.
id,name,role,active
1,John Doe,Admin,true
2,Jane Smith,User,false
3,Bob Johnson,Editor,true Copies the table as rich text with borders and a shaded header row. Every style is inline, because email clients strip stylesheets. Paste it into the body of a Gmail or Outlook message and it arrives as a table, not a wall of commas.
Copies indented <table>,
<thead> and
<tbody> markup with no classes or
inline styles, ready to drop into a template, a CMS
HTML block or a README. Cell values are escaped, so
a stray < will not break the page.
Spreadsheets convert values they recognize as numbers or dates: leading zeros disappear, 1-2 becomes a date, long IDs turn into scientific notation. This viewer shows each cell as the text in the file.
Checking whether a column exists or the row count is right takes one paste here instead of a spreadsheet launch.
const csv = `id,name,note
1,John Doe,"Says ""hi"", then leaves"
2,Jane Smith,Plain text`;
// Minimal RFC 4180 parser: handles quotes, escaped quotes and delimiters inside quotes
function parseCsv(text, delimiter = ",") {
const rows = [];
let row = [], field = "", inQuotes = false;
for (let i = 0; i < text.length; i++) {
const c = text[i];
if (inQuotes) {
if (c === '"' && text[i + 1] === '"') { field += '"'; i++; }
else if (c === '"') inQuotes = false;
else field += c;
} else if (c === '"') inQuotes = true;
else if (c === delimiter) { row.push(field); field = ""; }
else if (c === "\n") { row.push(field); rows.push(row); row = []; field = ""; }
else if (c !== "\r") field += c;
}
row.push(field); rows.push(row);
return rows;
}
const esc = (s) => String(s).replace(/&/g, "&").replace(/</g, "<");
const [header, ...body] = parseCsv(csv);
const html =
`<table><thead><tr>${header.map((h) => `<th>${esc(h)}</th>`).join("")}</tr></thead>` +
`<tbody>${body.map((r) => `<tr>${r.map((c) => `<td>${esc(c)}</td>`).join("")}</tr>`).join("")}</tbody></table>`;
console.log(html); For production use Papa Parse (browser) or csv-parse (Node.js); they handle line breaks inside quotes and streaming.
<?php
$csv = "id,name,note\n1,John Doe,\"Says \"\"hi\"\", then leaves\"\n2,Jane Smith,Plain text";
// str_getcsv splits one line; fgetcsv does the same from a file handle
$lines = array_filter(explode("\n", $csv), "strlen");
$rows = array_map(fn($line) => str_getcsv($line, ",", "\"", "\\"), $lines);
$header = array_shift($rows);
echo "<table>\n<thead><tr>";
foreach ($header as $h) echo "<th>" . htmlspecialchars($h) . "</th>";
echo "</tr></thead>\n<tbody>\n";
foreach ($rows as $row) {
echo "<tr>";
foreach ($row as $cell) echo "<td>" . htmlspecialchars($cell) . "</td>";
echo "</tr>\n";
}
echo "</tbody>\n</table>\n";
// From a file, streaming row by row:
// $fh = fopen("data.csv", "r");
// while (($row = fgetcsv($fh, 0, ";", "\"", "\\")) !== false) { ... } Pass the escape argument explicitly; PHP 8.4 deprecates relying on its default.
import csv
import io
from html import escape
data = 'id,name,note\n1,John Doe,"Says ""hi"", then leaves"\n2,Jane Smith,Plain text\n'
# csv.reader handles quoting; use delimiter=";" for semicolon files
reader = csv.reader(io.StringIO(data), delimiter=",")
header = next(reader)
parts = ["<table>", "<thead><tr>"]
parts += [f"<th>{escape(h)}</th>" for h in header]
parts.append("</tr></thead><tbody>")
for row in reader:
parts.append("<tr>" + "".join(f"<td>{escape(c)}</td>" for c in row) + "</tr>")
parts.append("</tbody></table>")
print("\n".join(parts))
# With pandas installed, one line does the same job:
# pandas.read_csv("data.csv", sep=",").to_html(index=False) Open files with newline="" when using the csv module, otherwise embedded line breaks are mangled.
CSV has no strict standard, only RFC 4180, which most tools loosely follow: commas between fields, CRLF between records, and any field holding a comma, quote or line break wrapped in double quotes with inner quotes doubled. Real files often break these rules, so this viewer parses line by line and tolerates missing quotes where it can.
Nothing gets reinterpreted. Cells are text, HTML characters are escaped, and a value like <script> is shown as those eight characters. Leading zeros, whitespace and empty fields come through exactly as they sit in the file, which a spreadsheet does not guarantee.
This tool follows the specifications below. Read them when you need the exact normative wording, for example to settle an edge case between two implementations.
Paste the CSV, click Copy for Email, then paste into the message body. The table is copied as rich text with inline borders and a header row, because inline styles are the only styling email clients keep.
Plain, indented <table> markup with <thead> and <tbody> and no classes or styles, so it picks up your site's CSS. Cell values are HTML-escaped.
Click Copy Markdown. You get a GitHub-flavored pipe table with the first CSV line as the header and a --- separator row. Pipes inside a field are escaped and line breaks become <br>, so one CSV record stays one table row. Paste it into a README, a GitHub issue, Notion or Obsidian. For alignment and width options, the CSV to Markdown tool does more.
Comma, semicolon, tab or pipe. Choose the one your file uses. Excel in many European locales exports with semicolons.
Yes. The first row becomes the table header and each following row becomes a table row.
Yes. The file is parsed in your browser tab, so the limit is your device's memory. Very wide tables scroll horizontally.
Yes. Fields wrapped in double quotes may contain the delimiter or line breaks and are displayed as a single cell.
Use the CSV to JSON tool with the same input; it produces an array of objects using the header names as keys.
Convert it with CSV to JSON, CSV to PHP array or CSV to JavaScript. JSON arrays have their own viewer at JSON to Table.