Table Layouts
Master table-based layouts for structured data presentation and page layout in PDF documents.
Learning Objectives
By the end of this article, you’ll be able to:
- Create responsive table layouts
- Control table sizing (fixed vs auto)
- Style table elements (rows, cells, borders)
- Use tables for page layout (beyond data)
- Create alternating row styles
- Handle table page breaks
- Build complex nested table structures
Tables for Data vs Layout
Tables serve two purposes in PDF documents:
1. Data Tables - Display tabular data
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>Product A</td>
<td>$99</td>
</tr>
</tbody>
</table>
2. Layout Tables - Structure page layout (using CSS, not <table>)
.container {
display: table;
}
.column {
display: table-cell;
}
Basic Table Structure
HTML Structure
<table>
<thead> <!-- Header rows -->
<tr>
<th>Column 1</th>
<th>Column 2</th>
</tr>
</thead>
<tbody> <!-- Data rows -->
<tr>
<td>Data 1</td>
<td>Data 2</td>
</tr>
</tbody>
<tfoot> <!-- Footer rows (optional) -->
<tr>
<td>Total</td>
<td>$100</td>
</tr>
</tfoot>
</table>
Basic Styling
table {
width: 100%;
border-collapse: collapse; /* Remove spacing between cells */
}
th, td {
padding: 10pt;
text-align: left;
border: 1pt solid #d1d5db;
}
thead {
background-color: #2563eb;
color: white;
}
Table Sizing
table-layout: auto (Default)
Columns size based on content.
table {
table-layout: auto; /* Default */
width: 100%;
}
Characteristics:
- Flexible column widths
- Columns adjust to content
- Slower rendering for large tables
- Can create uneven columns
table-layout: fixed
Columns size based on specified widths.
table {
table-layout: fixed; /* Faster, predictable */
width: 100%;
}
col:nth-child(1) { width: 30%; }
col:nth-child(2) { width: 40%; }
col:nth-child(3) { width: 30%; }
Characteristics:
- Fixed, predictable widths
- Faster rendering
- Content may overflow if too long
- Better for consistent layouts
Column Width Control
Using <col> Elements
<table>
<colgroup>
<col style="width: 30%;" />
<col style="width: 50%;" />
<col style="width: 20%;" />
</colgroup>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>Product A</td>
<td>Description text</td>
<td>$99</td>
</tr>
</tbody>
</table>
Using CSS
/* Specific columns */
table col:nth-child(1) { width: 200pt; }
table col:nth-child(2) { width: auto; } /* Takes remaining */
table col:nth-child(3) { width: 100pt; }
/* Or via cells */
thead th:nth-child(1) { width: 200pt; }
thead th:nth-child(2) { width: auto; }
thead th:nth-child(3) { width: 100pt; }
Border Styles
Collapsed Borders (Recommended)
table {
border-collapse: collapse; /* Single borders */
}
th, td {
border: 1pt solid #d1d5db;
}
Separated Borders
table {
border-collapse: separate;
border-spacing: 5pt; /* Space between cells */
}
th, td {
border: 1pt solid #d1d5db;
}
No Borders
table {
border-collapse: collapse;
}
th, td {
border: none;
border-bottom: 1pt solid #e5e7eb; /* Only bottom borders */
}
Alternating Row Colors
Using CSS
/* Even rows */
tbody tr:nth-child(even) {
background-color: #f9fafb;
}
/* Odd rows */
tbody tr:nth-child(odd) {
background-color: white;
}
Zebra Striping
tbody tr:nth-child(odd) {
background-color: #f9fafb;
}
tbody tr:nth-child(even) {
background-color: white;
}
/* Hover effect not applicable in PDF, but can style specific rows */
tbody tr.highlighted {
background-color: #fef3c7;
}
Table Page Breaks
Keep Table Together
table {
page-break-inside: avoid; /* Don't split table */
}
Allow Breaks, Keep Rows Together
table {
page-break-inside: auto; /* Can split */
}
tr {
page-break-inside: avoid; /* Don't split rows */
}
Repeat Headers on Each Page
thead {
display: table-header-group; /* Repeat on each page */
}
tfoot {
display: table-footer-group; /* Repeat on each page */
}
Practical Examples
Example 1: Data Table with Styling
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml'>
<head>
<title>Sales Report</title>
<style>
@page {
size: Letter;
margin: 1in;
}
* {
box-sizing: border-box;
}
body {
font-family: Helvetica, sans-serif;
font-size: 11pt;
margin: 0;
}
h1 {
color: #1e40af;
font-size: 24pt;
margin-top: 0;
margin-bottom: 30pt;
padding-bottom: 15pt;
border-bottom: 2pt solid #2563eb;
}
/* ==============================================
TABLE STYLING
============================================== */
table {
width: 100%;
border-collapse: collapse;
margin: 20pt 0;
table-layout: fixed;
}
/* Column widths */
col:nth-child(1) { width: 25%; } /* Name */
col:nth-child(2) { width: 20%; } /* Region */
col:nth-child(3) { width: 20%; } /* Sales */
col:nth-child(4) { width: 15%; } /* Growth */
col:nth-child(5) { width: 20%; } /* Target */
/* Header */
thead {
background-color: #2563eb;
color: white;
}
th {
padding: 12pt;
text-align: left;
font-weight: bold;
border: none;
}
/* Body */
td {
padding: 10pt 12pt;
border-bottom: 1pt solid #e5e7eb;
}
/* Alternating rows */
tbody tr:nth-child(even) {
background-color: #f9fafb;
}
/* Footer */
tfoot {
background-color: #eff6ff;
font-weight: bold;
}
tfoot td {
padding: 12pt;
border-top: 2pt solid #2563eb;
border-bottom: none;
}
/* ==============================================
SPECIAL CELLS
============================================== */
.number {
text-align: right;
}
.positive {
color: #059669;
}
.negative {
color: #dc2626;
}
.center {
text-align: center;
}
</style>
</head>
<body>
<h1>Q4 2024 Sales Report</h1>
<table>
<colgroup>
<col />
<col />
<col />
<col />
<col />
</colgroup>
<thead>
<tr>
<th>Salesperson</th>
<th>Region</th>
<th class="number">Sales</th>
<th class="number">Growth</th>
<th class="number">Target</th>
</tr>
</thead>
<tbody>
<tr>
<td>John Smith</td>
<td>Northeast</td>
<td class="number">$450,000</td>
<td class="number positive">+15%</td>
<td class="number">$400,000</td>
</tr>
<tr>
<td>Sarah Johnson</td>
<td>Southeast</td>
<td class="number">$380,000</td>
<td class="number positive">+8%</td>
<td class="number">$375,000</td>
</tr>
<tr>
<td>Michael Brown</td>
<td>Midwest</td>
<td class="number">$290,000</td>
<td class="number negative">-3%</td>
<td class="number">$300,000</td>
</tr>
<tr>
<td>Emily Davis</td>
<td>West</td>
<td class="number">$520,000</td>
<td class="number positive">+22%</td>
<td class="number">$425,000</td>
</tr>
<tr>
<td>David Wilson</td>
<td>Southwest</td>
<td class="number">$340,000</td>
<td class="number positive">+12%</td>
<td class="number">$325,000</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="2">Total</td>
<td class="number">$1,980,000</td>
<td class="number positive">+10.8%</td>
<td class="number">$1,825,000</td>
</tr>
</tfoot>
</table>
<p style="margin-top: 30pt; color: #666; font-size: 10pt;">
<strong>Note:</strong> Growth percentages are compared to Q4 2023.
</p>
</body>
</html>
Example 2: Nested Table for Layout
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml'>
<head>
<title>Invoice with Nested Tables</title>
<style>
@page {
size: Letter;
margin: 1in;
}
* {
box-sizing: border-box;
}
body {
font-family: Helvetica, sans-serif;
font-size: 11pt;
margin: 0;
}
/* ==============================================
LAYOUT TABLE (using display: table)
============================================== */
.header-layout {
display: table;
width: 100%;
margin-bottom: 30pt;
}
.header-left {
display: table-cell;
width: 60%;
vertical-align: top;
}
.header-right {
display: table-cell;
width: 40%;
vertical-align: top;
text-align: right;
}
.company-name {
font-size: 24pt;
font-weight: bold;
color: #1e40af;
margin-bottom: 10pt;
}
.invoice-number {
font-size: 18pt;
color: #2563eb;
margin-bottom: 5pt;
}
/* ==============================================
DATA TABLES
============================================== */
table.data-table {
width: 100%;
border-collapse: collapse;
margin: 20pt 0;
}
.data-table th {
background-color: #2563eb;
color: white;
padding: 10pt;
text-align: left;
font-weight: bold;
}
.data-table td {
padding: 8pt 10pt;
border-bottom: 1pt solid #e5e7eb;
}
.data-table tbody tr:nth-child(even) {
background-color: #f9fafb;
}
.data-table tfoot {
background-color: #eff6ff;
font-weight: bold;
}
.data-table tfoot td {
padding: 10pt;
border-top: 2pt solid #2563eb;
border-bottom: none;
}
.text-right {
text-align: right;
}
.text-center {
text-align: center;
}
.total-row {
font-size: 14pt;
color: #1e40af;
}
</style>
</head>
<body>
<!-- Header with layout table (CSS display: table) -->
<div class="header-layout">
<div class="header-left">
<div class="company-name">Acme Corporation</div>
<p style="margin: 0;">123 Business Street, Suite 100<br/>
New York, NY 10001<br/>
(555) 123-4567</p>
</div>
<div class="header-right">
<div class="invoice-number">Invoice #2025-001</div>
<p style="margin: 0;">Date: January 15, 2025<br/>
Due: February 14, 2025</p>
</div>
</div>
<!-- Bill To -->
<div style="margin-bottom: 30pt;">
<h2 style="color: #2563eb; font-size: 16pt; margin: 0 0 10pt 0;">Bill To:</h2>
<p style="margin: 0;"><strong>XYZ Company</strong><br/>
456 Commerce Ave<br/>
Boston, MA 02101</p>
</div>
<!-- Line Items (data table) -->
<table class="data-table">
<thead>
<tr>
<th>Description</th>
<th class="text-center">Qty</th>
<th class="text-right">Rate</th>
<th class="text-right">Amount</th>
</tr>
</thead>
<tbody>
<tr>
<td>Consulting Services</td>
<td class="text-center">10</td>
<td class="text-right">$150.00</td>
<td class="text-right">$1,500.00</td>
</tr>
<tr>
<td>Software Development</td>
<td class="text-center">20</td>
<td class="text-right">$200.00</td>
<td class="text-right">$4,000.00</td>
</tr>
<tr>
<td>Project Management</td>
<td class="text-center">5</td>
<td class="text-right">$175.00</td>
<td class="text-right">$875.00</td>
</tr>
<tr>
<td>Testing & QA</td>
<td class="text-center">8</td>
<td class="text-right">$125.00</td>
<td class="text-right">$1,000.00</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="3" class="text-right">Subtotal:</td>
<td class="text-right">$7,375.00</td>
</tr>
<tr>
<td colspan="3" class="text-right">Tax (8%):</td>
<td class="text-right">$590.00</td>
</tr>
<tr class="total-row">
<td colspan="3" class="text-right">Total:</td>
<td class="text-right">$7,965.00</td>
</tr>
</tfoot>
</table>
<!-- Footer -->
<div style="margin-top: 40pt; padding-top: 20pt; border-top: 1pt solid #d1d5db; font-size: 9pt; color: #666;">
<p><strong>Payment Terms:</strong> Payment due within 30 days.</p>
<p><strong>Note:</strong> Thank you for your business!</p>
</div>
</body>
</html>
Example 3: Complex Multi-Section Table
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml'>
<head>
<title>Product Comparison Table</title>
<style>
@page {
size: Letter landscape; /* Wider page for comparison */
margin: 0.75in;
}
* {
box-sizing: border-box;
}
body {
font-family: Helvetica, sans-serif;
font-size: 10pt;
margin: 0;
}
h1 {
text-align: center;
color: #1e40af;
font-size: 24pt;
margin: 0 0 30pt 0;
}
/* ==============================================
COMPARISON TABLE
============================================== */
table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}
/* Column widths */
col:nth-child(1) { width: 30%; } /* Feature */
col:nth-child(2) { width: 17.5%; } /* Basic */
col:nth-child(3) { width: 17.5%; } /* Standard */
col:nth-child(4) { width: 17.5%; } /* Pro */
col:nth-child(5) { width: 17.5%; } /* Enterprise */
/* Header */
thead th {
padding: 15pt;
text-align: center;
font-weight: bold;
border: 1pt solid #d1d5db;
}
thead th:first-child {
background-color: #f9fafb;
text-align: left;
}
thead th:nth-child(2) { background-color: #e0e7ff; color: #1e40af; }
thead th:nth-child(3) { background-color: #dbeafe; color: #1e3a8a; }
thead th:nth-child(4) { background-color: #bfdbfe; color: #1e40af; }
thead th:nth-child(5) { background-color: #93c5fd; color: #1e3a8a; }
/* Subheaders */
.subheader {
background-color: #1e40af;
color: white;
font-weight: bold;
padding: 8pt 15pt;
}
.subheader td {
border: 1pt solid #1e40af;
}
/* Body */
td {
padding: 10pt 15pt;
border: 1pt solid #e5e7eb;
}
tbody tr td:first-child {
background-color: #f9fafb;
font-weight: 500;
}
tbody tr:nth-child(even) td:not(:first-child) {
background-color: #fafafa;
}
/* Checkmarks and X marks */
.check {
text-align: center;
color: #059669;
font-weight: bold;
font-size: 14pt;
}
.cross {
text-align: center;
color: #dc2626;
font-weight: bold;
font-size: 14pt;
}
.value {
text-align: center;
font-weight: bold;
color: #2563eb;
}
</style>
</head>
<body>
<h1>Product Plans Comparison</h1>
<table>
<colgroup>
<col />
<col />
<col />
<col />
<col />
</colgroup>
<thead>
<tr>
<th>Feature</th>
<th>Basic<br/><span style="font-weight: normal;">$29/mo</span></th>
<th>Standard<br/><span style="font-weight: normal;">$79/mo</span></th>
<th>Pro<br/><span style="font-weight: normal;">$149/mo</span></th>
<th>Enterprise<br/><span style="font-weight: normal;">Custom</span></th>
</tr>
</thead>
<tbody>
<!-- Storage Section -->
<tr class="subheader">
<td colspan="5">Storage & Users</td>
</tr>
<tr>
<td>Users</td>
<td class="value">5</td>
<td class="value">15</td>
<td class="value">50</td>
<td class="value">Unlimited</td>
</tr>
<tr>
<td>Storage</td>
<td class="value">10 GB</td>
<td class="value">100 GB</td>
<td class="value">500 GB</td>
<td class="value">Unlimited</td>
</tr>
<tr>
<td>File Upload Limit</td>
<td class="value">50 MB</td>
<td class="value">200 MB</td>
<td class="value">2 GB</td>
<td class="value">10 GB</td>
</tr>
<!-- Features Section -->
<tr class="subheader">
<td colspan="5">Core Features</td>
</tr>
<tr>
<td>Document Management</td>
<td class="check">✓</td>
<td class="check">✓</td>
<td class="check">✓</td>
<td class="check">✓</td>
</tr>
<tr>
<td>Collaboration Tools</td>
<td class="cross">✗</td>
<td class="check">✓</td>
<td class="check">✓</td>
<td class="check">✓</td>
</tr>
<tr>
<td>Advanced Analytics</td>
<td class="cross">✗</td>
<td class="cross">✗</td>
<td class="check">✓</td>
<td class="check">✓</td>
</tr>
<tr>
<td>API Access</td>
<td class="cross">✗</td>
<td class="cross">✗</td>
<td class="check">✓</td>
<td class="check">✓</td>
</tr>
<tr>
<td>Custom Integrations</td>
<td class="cross">✗</td>
<td class="cross">✗</td>
<td class="cross">✗</td>
<td class="check">✓</td>
</tr>
<!-- Support Section -->
<tr class="subheader">
<td colspan="5">Support</td>
</tr>
<tr>
<td>Email Support</td>
<td class="check">✓</td>
<td class="check">✓</td>
<td class="check">✓</td>
<td class="check">✓</td>
</tr>
<tr>
<td>Priority Support</td>
<td class="cross">✗</td>
<td class="check">✓</td>
<td class="check">✓</td>
<td class="check">✓</td>
</tr>
<tr>
<td>Dedicated Account Manager</td>
<td class="cross">✗</td>
<td class="cross">✗</td>
<td class="cross">✗</td>
<td class="check">✓</td>
</tr>
<tr>
<td>24/7 Phone Support</td>
<td class="cross">✗</td>
<td class="cross">✗</td>
<td class="check">✓</td>
<td class="check">✓</td>
</tr>
</tbody>
</table>
</body>
</html>
Try It Yourself
Exercise 1: Sales Report
Create a sales report table with:
- Alternating row colors
- Right-aligned numbers
- Color-coded values (positive/negative)
- Footer with totals
Exercise 2: Product Comparison
Create a comparison table with:
- Multiple products as columns
- Features as rows
- Checkmarks for included features
- Subheaders to group features
Exercise 3: Invoice Layout
Create an invoice using:
- Layout tables (CSS display: table) for header
- Data table for line items
- Calculated totals in footer
- Professional styling
Common Pitfalls
❌ Not Setting table-layout
table {
width: 100%;
/* Missing table-layout - unpredictable column widths */
}
✅ Solution: Use table-layout: fixed
table {
width: 100%;
table-layout: fixed; /* Predictable widths */
}
❌ Forgetting border-collapse
table {
/* Missing border-collapse - double borders */
}
td {
border: 1pt solid black;
}
✅ Solution: Set border-collapse
table {
border-collapse: collapse; /* Single borders */
}
❌ Not Controlling Page Breaks
table {
/* Long table splits awkwardly */
}
✅ Solution: Control breaks
table {
page-break-inside: auto; /* Can split */
}
tr {
page-break-inside: avoid; /* Don't split rows */
}
thead {
display: table-header-group; /* Repeat on each page */
}
❌ Inconsistent Column Widths
/* No widths specified - inconsistent sizing */
th, td {
padding: 10pt;
}
✅ Solution: Specify widths
col:nth-child(1) { width: 40%; }
col:nth-child(2) { width: 30%; }
col:nth-child(3) { width: 30%; }
❌ Mixing Layout and Data Tables
<!-- Confusing structure -->
<table>
<tr>
<td>
<table><!-- Nested table --></table>
</td>
</tr>
</table>
✅ Solution: Use CSS display: table for layout
.layout {
display: table;
}
.column {
display: table-cell;
}
Table Layout Checklist
- table-layout specified (fixed or auto)
- border-collapse set to collapse
- Column widths defined (percentages or fixed)
- Page breaks controlled (avoid splitting rows)
- thead repeats on multiple pages if needed
- Alternating row colors if appropriate
- Consistent padding on all cells
- Proper use of thead, tbody, tfoot
Best Practices
- Use table-layout: fixed - Predictable, faster rendering
- Set border-collapse: collapse - Clean single borders
- Define column widths - Consistent layout across pages
- Control page breaks - Don’t split rows
- Use semantic HTML - thead, tbody, tfoot for structure
- Repeat headers - display: table-header-group for long tables
- Alternate row colors - Improves readability
- Right-align numbers - Standard convention for numeric data
Next Steps
Now that you master table layouts:
- Headers & Footers - Repeating page elements
- Layout Best Practices - Professional patterns
- Content Components - Images, lists, and more
Continue learning → Headers & Footers