Excel (XLSX) Export
TableCrafter exports your table to a genuine OOXML .xlsx workbook assembled server-side with PHP's ZipArchive — a real spreadsheet that opens natively in Excel, Google Sheets, and LibreOffice, never a CSV or HTML file renamed to .xlsx.
Overview
When a visitor picks Excel from a table's export menu, the browser sends the currently displayed rows and column configuration to WordPress over AJAX. The plugin's export handler (includes/class-tc-export-handler.php) builds a valid OOXML package on disk, stores it in a protected uploads directory, and returns a one-time download link. The result is a true .xlsx workbook with the correct MIME type application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.
Two key implementation details make this honest:
- The workbook is produced with
ZipArchive, writing the same internal parts a copy of Excel itself writes — not a tab-separated text dump. - The frontend label excel is treated as an alias and normalized to the canonical format key xlsx before generation, so a single export pipeline produces every format.
Enabling export on a table
Excel export rides on the same export switch as CSV and PDF. Add the export attribute to the [tablecrafter] shortcode:
<!-- Minimal: enable the export menu (CSV / Excel / PDF) -->
[tablecrafter source="https://example.com/sales.csv" export="true"]
A richer table that searches, paginates, and exports the filtered view:
[tablecrafter
source="https://example.com/q3-orders.csv"
search="true"
filters="true"
export="true"
per_page="25"
sort="total:desc"]
In the block editor, the same control is exposed as the Enable Export Tools toggle on the TableCrafter (Data Table) block, which sets the data-export attribute the frontend reads.
Shortcode attributes relevant to export
| Attribute | Required | Description |
|---|---|---|
| source | Required | URL of the data source (CSV, Google Sheet, Airtable, etc.). Without it the table cannot render or export. |
| export | Optional | Set to true (also accepts 1 / yes) to render the export controls. Defaults to false. |
| filters | Optional | Enables column filters. The Excel export reflects the filtered view (see below). Defaults to true. |
| search | Optional | Enables global search; matching rows are what gets exported. |
| include / exclude | Optional | Comma-separated column lists that shape which columns appear in the table, and therefore in the workbook. |
Using the export menu
With export="true", TableCrafter renders an export control group (.tc-export-controls) above the table. When more than one format is available, the controls render as a dropdown:
.tc-export-dropdown-wrapper— the container..tc-export-main-btn— the "Export Data ▼" trigger button..tc-export-dropdown— the menu, hidden until the trigger is clicked and closed on any outside click..tc-export-option.tc-export-excel— the Excel choice, shown with a 📊 icon and the label "Excel - Advanced spreadsheet with formatting".
Choosing Excel triggers a loading overlay (.tc-export-loading, "Generating EXCEL export..."), then the file downloads automatically and a success notification reports the file size.
A "Copy to Clipboard" button (.tc-copy-clipboard) sits beside the export menu. It copies tab-separated values you can paste straight into a spreadsheet cell — handy for quick, in-place transfers without generating a file.
What's inside the .xlsx file
The handler builds a minimal-but-valid OOXML package. Each part below is added to the ZIP with ZipArchive::addFromString():
| Package part | Purpose |
|---|---|
| [Content_Types].xml | Declares the content types for the workbook and worksheet parts so Excel knows how to read them. |
| _rels/.rels | Package-level relationship pointing at the workbook as the office document. |
| xl/workbook.xml | The workbook definition. Holds one sheet named TableCrafter Export. |
| xl/_rels/workbook.xml.rels | Relationship linking the workbook to its worksheet. |
| xl/worksheets/sheet1.xml | The actual data: one <row> per record with addressed <c> cells. |
Cells are written as inline strings (t="inlineStr" with <is><t>), and every value is HTML-escaped, so commas, quotes, and Unicode survive intact without the encoding surprises that plague CSV. Cell references use proper spreadsheet addressing (A1, B1, ... AA1) generated from the column index.
<!-- Shape of xl/worksheets/sheet1.xml (abbreviated) -->
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<sheetData>
<row r="1">
<c r="A1" t="inlineStr"><is><t>Product</t></is></c>
<c r="B1" t="inlineStr"><is><t>Total</t></is></c>
</row>
<row r="2">
<c r="A2" t="inlineStr"><is><t>Widget</t></is></c>
<c r="B2" t="inlineStr"><is><t>42.00</t></is></c>
</row>
</sheetData>
</worksheet>
The first row is a header row built from your column labels (the human-friendly label for each column, falling back to the raw field key). Header output can be suppressed by callers that set include_headers to false; the frontend export includes them by default.
What gets exported: rows, columns, and formatting
The Excel export honors the table's live state rather than re-fetching the raw source:
- Filtered & searched rows. Because
exportFilteredis on by default, only rows matching the current search and filters are written to the workbook. - Visible columns. Columns are taken from the table's column config; any column flagged
exportable: falseis omitted from the file. - Cleaned values. Each cell is normalized server-side: HTML tags are stripped, date-looking strings are reformatted to the template's
date_format, and numeric values are formatted vianumber_format. Arrays are JSON-encoded.
Export templates control the formatting and optional metadata. They are filterable, so you can register your own:
// Add a custom export template (e.g. in your theme functions.php)
add_filter('tc_export_templates', function ($templates) {
$templates['finance'] = [
'name' => 'Finance Report',
'include_metadata' => true,
'date_format' => 'M j, Y',
'number_format' => '$0.00',
];
return $templates;
});
Built-in templates are default, business (adds a metadata footer with generation time and record count), and data_analysis (ISO-8601 dates, high-precision numbers).
Opening the file in Excel and Sheets
- Microsoft Excel (desktop or web): double-click the downloaded
.xlsx. It opens directly to the "TableCrafter Export" sheet with no import wizard and no "this file's format doesn't match its extension" warning, because the bytes are real OOXML. - Google Sheets: use File → Import (or drag into Drive). Sheets recognizes the workbook and converts it; choose "Replace spreadsheet" or "Insert new sheet" as needed.
- LibreOffice / Numbers: open normally — both read the standard
.spreadsheetmlformat.
All cells are written as text (inline strings). This guarantees lossless values — leading zeros, long IDs, and codes are preserved exactly. If you need a column to behave as a number or date inside the workbook, apply Excel/Sheets cell formatting after opening, or set up your source so the value is unambiguous.
How the download is delivered and secured
Excel export is a two-step AJAX round trip, both endpoints nonce-protected:
- The browser POSTs the rows, columns, filename, and options to
admin-ajax.phpwithaction=tc_export_data, guarded by thetc_export_nonceand areadcapability check. The handler builds the.xlsx, stores the result in a transient (tc_export_*) for five minutes, and returns a one-timedownload_url. - Clicking that URL hits
action=tc_download_export(guarded bytc_download_nonce), which streams the file with the correctContent-TypeandContent-Disposition: attachmentheaders, then deletes both the temp file and the transient.
Generated files live in a protected directory, wp-content/uploads/tablecrafter-exports/, created on demand with an .htaccess "deny all" rule and a silent index.php so they can't be browsed directly. Filenames are prefixed tc_export_ with a UUID. Stale files are purged after download and, as a backstop, a cleanup routine removes any export older than an hour.
| Detail | Value |
|---|---|
| AJAX action (generate) | tc_export_data |
| AJAX action (download) | tc_download_export |
| Nonces | tc_export_nonce, tc_download_nonce |
| MIME type | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
| Temp directory | uploads/tablecrafter-exports/ (deny-all) |
| Link lifetime | 5 minutes (transient) |
Hooking into the export
On the JavaScript side, the onExport callback fires after a successful Excel download, receiving the format, the exported data and columns, the resolved filename, and the byte size — useful for analytics or post-export UI:
new TableCrafter('#my-table', {
data: 'https://example.com/orders.csv',
exportable: true,
onExport: (e) => {
// e.format === 'excel', plus e.filename, e.size, e.data, e.columns
console.log('Exported', e.filename, e.size);
}
});
XLSX generation requires PHP's ZipArchive extension. If it's unavailable, the handler throws and the export returns an error instead of producing a corrupt file. Confirm ext-zip is enabled on your host before relying on Excel export.
Next steps
For the lighter, header-row-and-rows format see export-csv.html, and for a paginated print-ready document see export-pdf.html.