CSV & Clipboard Export
TableCrafter lets visitors pull a live table straight into their spreadsheet: download a UTF-8 CSV file or copy the rows to the clipboard as tab-separated values. Both paths respect active filters and hidden columns, so people export exactly what they see.
Overview
Export is off by default. You turn it on per table with the export shortcode attribute, which renders an export toolbar above the grid. The toolbar always contains a Copy to Clipboard button, plus either a single Export CSV button or a multi-format dropdown depending on configuration.
Two engines power the output:
- Client-side CSV — built entirely in the browser from a
Bloband offered as a download. No server round-trip, instant. - Server-side export — posts the visible rows to WordPress over AJAX (
tc_export_data), whereTC_Export_Handlerwrites a genuine file (CSV, XLSX, or PDF) to a protected temp folder and streams it back. This path handles the richer XLSX and PDF formats and the BOM-prefixed CSV.
Clipboard copy is always 100% client-side and never touches the server.
This page reflects TableCrafter (wp-data-tables) v3.5.6. The export handler lives in includes/class-tc-export-handler.php; the front-end logic is in assets/js/tablecrafter.js.
Enabling export
Add the export attribute to your shortcode. It accepts the usual truthy strings (true, 1, yes); anything else is treated as false.
// Minimal: turn on the export toolbar
[tablecrafter source="https://example.com/data.csv" export="true"]
// Export alongside search and filters
[tablecrafter source="https://example.com/inventory.json" search="true" filters="true" export="true"]
The shortcode writes data-export="true" onto the container, and the front-end reads it into config.exportable at init. When exportable is true, TableCrafter calls renderExportControls() and inserts the .tc-export-controls toolbar before the table.
Shortcode attributes
| Attribute | Default | Description |
|---|---|---|
| export | false Optional | Shows the export toolbar (CSV/format buttons + Copy to Clipboard). Accepts true/1/yes. |
| source | "" Required | URL of the CSV, JSON, or Google Sheet feeding the table. Without it nothing renders to export. |
| include | "" Optional | Comma-separated columns to keep. Narrows what is displayed and therefore exported. |
| exclude | "" Optional | Comma-separated columns to drop from the table and exports. |
| filters | true Optional | Enables column filters. Active filters carry into the export (see Filter-aware exports). |
| search | false Optional | Enables global search; matching rows define what gets exported. |
There is no export_formats or export_filename shortcode attribute in v3.5.6. The format list (csv, excel, pdf) and the base filename (table-export) come from JavaScript config defaults (config.advancedExport.formats and config.exportFilename), not from the shortcode. Override them via the JS API if you initialise tables programmatically.
The export toolbar
renderExportControls() builds the toolbar like this:
- If the advanced dropdown is enabled and more than one format is configured, it renders
.tc-export-dropdown-wrapperwith a.tc-export-main-btn("Export Data ▼") that opens a panel of.tc-export-optionbuttons. - Otherwise it falls back to a single green
.tc-export-csvbutton labelled "Export CSV". - A
.tc-copy-clipboardbutton labelled "Copy to Clipboard" is always appended.
Format options in the dropdown route through handleExportFormat(): csv calls the client-side downloadCSV(), while excel and pdf call downloadEnhanced() (server-side). The fallback "Export CSV" button calls downloadEnhanced('csv'), which produces the BOM-prefixed server CSV.
Toolbar CSS classes
| Class | Element |
|---|---|
| .tc-export-controls | Toolbar wrapper around all export buttons. |
| .tc-export-csv | Single "Export CSV" button (green). |
| .tc-export-dropdown-wrapper | Wrapper for the multi-format dropdown. |
| .tc-export-main-btn | "Export Data" trigger button for the dropdown. |
| .tc-export-dropdown | The dropdown panel (hidden until opened). |
| .tc-export-option | One format row inside the dropdown (e.g. .tc-export-csv, .tc-export-pdf). |
| .tc-copy-clipboard | "Copy to Clipboard" button. |
| .tc-copy-success | JS-toggled state class added for ~2s after a successful copy. |
How CSV download works
The browser CSV path is straightforward. exportToCSV() joins the visible column labels into a header row, escapes each cell with escapeCSVField(), and joins rows with \n. downloadCSV() then wraps that string in a Blob and triggers a download via a temporary anchor:
// Simplified from downloadCSV()
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
link.download = this.config.exportFilename + '.csv'; // e.g. table-export.csv
link.click();
URL.revokeObjectURL(url);
escapeCSVField() follows RFC 4180: any value containing a comma, newline, or double-quote is wrapped in quotes with internal quotes doubled (" → ""). Pure numeric values are left unquoted; other text is quoted.
The server-side CSV (used by downloadEnhanced('csv') and TC_Export_Handler::export_csv()) goes further: it prepends a UTF-8 BOM and writes rows with PHP's fputcsv() using an explicit empty escape argument to keep clean RFC 4180 quoting and avoid the PHP 8.4 deprecation.
Character encoding
Encoding is the difference between a clean export and one full of garbled accented characters in Excel.
- Client Blob CSV declares
charset=utf-8on the MIME type. UTF-8 content is preserved, but no BOM is written. - Server CSV writes the UTF-8 byte-order mark
\xEF\xBB\xBFas the first bytes of the file. This is what tells Microsoft Excel to read the file as UTF-8 rather than the local code page, so emoji, accents, and non-Latin scripts open correctly. - XLSX stores every cell as an OOXML
inlineStr, escaped withhtmlspecialchars()— Unicode-safe by construction.
If recipients open exports in Excel on Windows and see mojibake (for example é instead of é), route them through a format that uses the server handler — the "Export CSV" fallback button or the XLSX option — so they get the BOM-prefixed file. The pure client-side dropdown CSV omits the BOM.
Copy to clipboard
The .tc-copy-clipboard button calls copyToClipboard(), which serialises the exportable rows as tab-separated values (TSV) — the format spreadsheets expect when you paste. Labels form the header line, then each row's fields are joined with \t and rows with \n.
It prefers the modern asynchronous Clipboard API and degrades gracefully:
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(onSuccess).catch(onError);
} else {
onError('Clipboard API unavailable'); // falls back to a hidden textarea + execCommand('copy')
}
On success the button text swaps to "Copied!" and gains the .tc-copy-success class for two seconds before reverting. If the Clipboard API is missing or blocked (insecure context, permissions), the fallback creates an off-screen <textarea>, selects it, and runs document.execCommand('copy').
The async Clipboard API requires a secure context (HTTPS or localhost). On plain HTTP the code automatically uses the legacy execCommand path, so copy still works on most browsers.
Filter-aware & column-aware exports
Both CSV and clipboard pull their rows from getExportableData() and columns from getExportableColumns(), so exports honour what the visitor has narrowed the table down to:
- Rows — when
config.exportFilteredis true (the default),getExportableData()returnsgetFilteredData(), i.e. only rows passing the active column filters and global search. Set it to false to always export the full dataset regardless of filters. - Columns —
getExportableColumns()drops any column whose config hasexportable: false. Use this to keep internal or sensitive columns out of downloads while still showing them on screen.
Because filtering is applied before serialisation, a user who filters to "Region = West" and exports gets only the West rows — no extra server filtering needed.
Server-side export flow
For the server path, downloadEnhanced() POSTs a FormData payload to admin-ajax.php:
- Action
tc_export_datawithformat, JSONdata, JSONcolumns,filename,options, and a nonce fromgetExportNonce()(thetc_export_nonceaction). ajax_export_data()verifies the nonce and areadcapability, then routes the client-supplied rows throughTC_Export_Handler::export_client_data()→export_data().- The generated file is written to
wp-uploads/tablecrafter-exports/(protected by an auto-generated.htaccessdeny rule) and cached in a 5-minute transient. - The response returns a one-time
download_url(actiontc_download_export, noncetc_download_nonce);triggerDownload()clicks it to save the file.
Old temp files are reaped by cleanup_old_exports() (default max age one hour), and the per-download file is removed after serving.
Server export requires a valid nonce and the read capability. Logged-out visitors are served via wp_ajax_nopriv_tc_export_data, but if the nonce can't be resolved the request is rejected. The instant client-side CSV and clipboard copy have no such requirement and always work in the browser.
The onExport callback
When you initialise a table through the JS API, supply config.onExport to hook into exports for analytics or post-processing. For client CSV it fires with the format, the exported rows, and the raw CSV string:
new TableCrafter(el, {
exportable: true,
exportFiltered: true,
exportFilename: 'sales-q3',
onExport(payload) {
// { format: 'csv', data: [...], csvData: '...' }
console.log('Exported', payload.format, payload.data.length, 'rows');
}
});
Next steps
To produce formatted spreadsheets and report-ready documents from the same toolbar, continue with export-xlsx-pdf.html, and pair exports with on-page narrowing in filtering-and-search.html.