CSV to HTML Table
Convert CSV to an HTML table
CSV Input
HTML Output
What is CSV to HTML Table Conversion?
CSV to HTML table conversion takes comma-separated values and transforms them into structured HTML markup that browsers can render as a visual table. The output uses standard HTML table elements defined in the HTML Living Standard: table, thead, tbody, tr, th, and td. This process maps each CSV row to a tr element and each field to a td or th cell.
CSV files store data as plain text with rows separated by newlines and fields separated by delimiters (typically commas). While CSV works well for data storage and transfer between applications like Excel, Google Sheets, and databases, it has no presentation layer. HTML tables add that layer by wrapping data in semantic markup that supports styling with CSS, sorting with JavaScript, and accessibility through attributes like scope and aria-label.
The conversion must handle several edge cases defined in RFC 4180: quoted fields containing commas or newlines, escaped double quotes within fields, and varying delimiters (semicolons, tabs, pipes). A correct converter also escapes HTML entities in cell content, replacing <, >, &, and quote characters with their entity equivalents to prevent broken markup or XSS vulnerabilities.
Why Use a CSV to HTML Table Converter?
Building HTML tables by hand is tedious and error-prone, especially for datasets with dozens of columns or hundreds of rows. This converter handles the parsing, escaping, and formatting in one step.
CSV to HTML Table Use Cases
HTML Table Element Reference
A well-structured HTML table uses semantic elements that separate headers, body, and footer. Screen readers and search engines use these elements to understand the table structure. Grouping rows with thead, tbody, and tfoot lets browsers apply independent scrolling and repeat header rows in print layouts.
| Element | Role | Notes |
|---|---|---|
| <table> | Table root | Wraps the entire table structure |
| <thead> | Header group | Contains header rows; browsers repeat on print page breaks |
| <tbody> | Body group | Contains data rows; enables independent scrolling with CSS |
| <tfoot> | Footer group | Summary or totals row; renders after tbody |
| <tr> | Table row | Groups cells horizontally |
| <th> | Header cell | Bold and centered by default; supports scope attribute for accessibility |
| <td> | Data cell | Standard content cell; accepts any inline or block HTML |
| <caption> | Table caption | Visible title above the table; read by screen readers first |
| <colgroup> | Column group | Applies width or style to entire columns without per-cell classes |
CSV vs HTML Table
CSV is a transport format optimized for simplicity, while HTML is a presentation format optimized for rendering, accessibility, and interactivity in browsers.
Code Examples
Here is how to convert CSV to HTML tables programmatically in different languages. Each example handles the header row separately and escapes HTML entities in cell content. These snippets are ready to drop into a script, a build pipeline, or a backend API endpoint that generates HTML reports from data exports.
// CSV string → HTML table with thead/tbody
const csv = `name,age,city
Alice,30,Berlin
Bob,25,Tokyo`
function csvToHtmlTable(csv) {
const rows = csv.trim().split('\n').map(r => r.split(','))
const [headers, ...data] = rows
const ths = headers.map(h => `<th>${h}</th>`).join('')
const trs = data.map(row =>
' <tr>' + row.map(c => `<td>${c}</td>`).join('') + '</tr>'
).join('\n')
return `<table>
<thead><tr>${ths}</tr></thead>
<tbody>
${trs}
</tbody>
</table>`
}
console.log(csvToHtmlTable(csv))
// → <table><thead><tr><th>name</th>...</tr></thead><tbody>...</tbody></table>import csv, io, html
csv_string = """name,age,city
Alice,30,Berlin
Bob,25,Tokyo"""
reader = csv.reader(io.StringIO(csv_string))
headers = next(reader)
lines = ['<table>', ' <thead>', ' <tr>']
for h in headers:
lines.append(f' <th>{html.escape(h)}</th>')
lines += [' </tr>', ' </thead>', ' <tbody>']
for row in reader:
lines.append(' <tr>')
for cell in row:
lines.append(f' <td>{html.escape(cell)}</td>')
lines.append(' </tr>')
lines += [' </tbody>', '</table>']
print('\n'.join(lines))
# → well-formed HTML table with escaped special characters<?php
$csv = "name,age,city\nAlice,30,Berlin\nBob,25,Tokyo";
$rows = array_map('str_getcsv', explode("\n", trim($csv)));
$headers = array_shift($rows);
echo "<table>\n <thead>\n <tr>\n";
foreach ($headers as $h) {
echo " <th>" . htmlspecialchars($h) . "</th>\n";
}
echo " </tr>\n </thead>\n <tbody>\n";
foreach ($rows as $row) {
echo " <tr>\n";
foreach ($row as $cell) {
echo " <td>" . htmlspecialchars($cell) . "</td>\n";
}
echo " </tr>\n";
}
echo " </tbody>\n</table>";
// → <table><thead>...<th>name</th>...</thead><tbody>...</tbody></table># Using awk — quick one-liner for simple CSV (no quoted fields)
awk -F, 'NR==1{print "<table><thead><tr>";for(i=1;i<=NF;i++)print "<th>"$i"</th>";print "</tr></thead><tbody>"}
NR>1{print "<tr>";for(i=1;i<=NF;i++)print "<td>"$i"</td>";print "</tr>"}
END{print "</tbody></table>"}' data.csv
# Using Python one-liner for RFC 4180-compliant parsing
python3 -c "
import csv, sys, html
r=csv.reader(sys.stdin); h=next(r)
print('<table><thead><tr>')
print(''.join(f'<th>{html.escape(c)}</th>' for c in h))
print('</tr></thead><tbody>')
for row in r:
print('<tr>'+''.join(f'<td>{html.escape(c)}</td>' for c in row)+'</tr>')
print('</tbody></table>')
" < data.csv