CSV to PDF report formatting: wide tables, repeating headers, clean page breaks
The three things that make a CSV-to-PDF report readable instead of a mess: the header row should repeat at the top of every page (put it in a <thead>, which browsers treat as a repeating header group when printing), rows should not split across a page boundary (break-inside: avoid on each <tr>), and wide tables need a deliberate strategy because a 30-column CSV will not fit on portrait A4 no matter what. If you just want this done for you, PDFMoka's CSV to PDF converter applies these rules automatically, with column sizing and repeated headers. If you are building the report yourself, here is exactly what to set.
Repeat the header row on every page
This is the single highest-value formatting fix, and it hinges on one structural rule: the header must live in a <thead> element, not loose <tr>s at the top of <tbody>.
<table>
<thead>
<tr><th>ID</th><th>Name</th><th>Department</th><th>Amount</th></tr>
</thead>
<tbody>
<tr><td>...</td><td>...</td><td>...</td><td>...</td></tr>
<!-- many rows -->
</tbody>
</table>
Browsers treat <thead> as a table-header-group and, when a table spans multiple printed pages, repeat that group at the top of each page automatically. If your headers only show on page one, the cause is almost always that the <th> cells are not wrapped in <thead>. You can make the intent explicit:
@media print {
thead { display: table-header-group; }
tfoot { display: table-footer-group; } /* repeats a footer row too, if you have one */
}
verify against your target browser: repeating table headers on print has historically been better supported in print-oriented engines than in older desktop browsers, though current Chrome, Firefox, and Safari handle it. Always check in print preview (Cmd/Ctrl+P) on the browser you actually target.
Stop rows from splitting across pages
Nothing makes a data report harder to read than a row whose top half is on page 2 and bottom half on page 3. Prevent it with break-inside: avoid on the rows (and keep it on the table too):
@media print {
tr { break-inside: avoid; }
table { break-inside: avoid; }
}
Note the property names: the modern break-inside has replaced the older page-break-inside for better cross-browser consistency, though many engines still accept the old name as an alias. Using break-inside is the current recommendation.
If you want a hard page break after a fixed number of rows (say, grouping 25 records per page), target rows with nth-child and break-after:
@media print {
tbody tr:nth-child(25n) { break-after: page; }
}
Use this sparingly, it is for grouped reports, not general data dumps, where letting the table flow naturally is usually cleaner.
Set the page box: size and margins
Define the printed page explicitly so headers are not clipped and margins are consistent:
@page {
size: A4; /* or "letter" for US */
margin: 18mm 16mm;
}
Without an explicit @page, you are at the mercy of the print driver's defaults, which is a common reason "it looked fine on screen" turns into clipped headers on paper.
The wide-table problem (the honest hard part)
Everything above assumes your columns fit the page width. The real pain with CSV reports is when they do not: a spreadsheet export with 20 or 40 columns simply cannot render legibly across a portrait page. There is no CSS trick that makes 40 columns fit and stay readable. Your realistic options, in order of how often they are the right call:
- Rotate to landscape.
@page { size: A4 landscape; }buys you significant horizontal room and is often enough for moderately wide tables. - Constrain column widths and let text wrap.
table-layout: fixedplus awidthper column stops one long field from blowing out the layout; long cell values wrap instead of forcing the table wider than the page. - Reduce font size for print, within reason. Dropping the table to 9 or 10pt for print recovers width, but do not go so small it is unreadable.
- Drop or reorder columns. The honest answer for a genuinely wide dataset: a printed PDF is not a spreadsheet, and cramming 40 columns onto paper serves no reader. Decide which columns matter for the report and leave the rest in the CSV.
We are being straight about this because a lot of "CSV to PDF" tools silently shrink an ultra-wide table until it is unreadable and call it done. For a truly wide dataset, the useful output is a report of the columns that matter, not a photograph of the whole spreadsheet.
Delimiters and encoding (before formatting even starts)
Two upstream issues that wreck a CSV report before layout matters:
- Delimiter. Not every "CSV" is comma-separated. European exports often use semicolons; some use tabs. A good converter detects the delimiter rather than assuming a comma, otherwise every row lands in one column.
- Encoding. A file saved as Latin-1 or Windows-1252 opened as UTF-8 turns accented characters into mojibake. Detecting or letting you set the encoding prevents garbled text.
PDFMoka's converter auto-detects the delimiter and handles common encodings; if you are rolling your own, do not hardcode a comma.
Doing it without building any of this
If you would rather not hand-tune print CSS, convert your CSV to a formatted PDF here. It sizes columns, repeats the header on every page, avoids splitting rows, and lets you pick page size and orientation for wide tables. It runs in your browser, so the data, which for CSVs is often a customer list, financial export, or other sensitive table, is never uploaded. If your data is in a PDF and you need it back as CSV, see PDF to CSV for the reverse direction (text-based tables only).
Quick checklist
- Header cells inside
<thead>so they repeat per page. break-inside: avoidontrso rows stay whole.- Explicit
@pagesize and margins. - For wide tables: landscape,
table-layout: fixedwith column widths, modest font reduction, or cut columns. - Confirm delimiter and encoding are read correctly before judging the layout.
References
- MDN,
break-inside(row/table page-break control): https://developer.mozilla.org/en-US/docs/Web/CSS/break-inside - MDN,
@pageat-rule (print page size and margins): https://developer.mozilla.org/en-US/docs/Web/CSS/@page