Export Data (CSV/JSON)
Relevant source files
The following files were used as context for generating this wiki page:
- packages/core/src/plugins/csv-export.ts
- packages/core/src/lib/export-format.ts
- packages/ui/src/components/table/export-button.tsx
- plans/050-export-ui.md
- packages/adapters/drizzle/tests/export-data.test.ts
- packages/ui/tests/hooks/use-table-export.test.tsx
- packages/core/tests/csv-export-plugin.test.ts
The Export Data system provides a comprehensive mechanism for extracting table data into portable formats, primarily CSV and JSON. It bridges the gap between the data adapter layer and the user interface, allowing users to download the current view of their data, including active filters and sorting states. The system is designed with security in mind, featuring automatic CSV formula-injection escaping to prevent malicious spreadsheet execution.
Data export is implemented as a multi-tier feature: an adapter-level contract for server-side generation, a core plugin for post-fetch capture, and a UI layer providing hooks and components for browser-based downloads. The system enforces a default row cap to prevent memory exhaustion (Out of Memory errors) during large exports, while allowing explicit overrides when needed.
Sources: plans/050-export-ui.md:27-35, packages/core/src/lib/export-format.ts:25-36
Architecture and Data Flow
The export architecture follows a request-response flow initiated by the UI and fulfilled by the database adapter or a core plugin.
Export Sequence
The following diagram illustrates the lifecycle of an export request from the user interface down to the adapter layer.
Components and Hooks
| Component | Description |
|---|---|
ExportButton | A shadcn-style UI component that renders a format menu (CSV/JSON). It is hidden if the adapter does not support export. |
useTableExport | A React hook that encapsulates the logic for calling the adapter's export method and managing browser download lifecycle. |
csvExport() | A core plugin that uses the afterFetch seam to capture results and provides toCsv() and toJson() methods. |
convertToExportFormat | A utility function responsible for serializing row data and applying CSV security escaping. |
Sources: packages/ui/src/components/table/export-button.tsx:1-40, packages/core/src/plugins/csv-export.ts:1-50, plans/050-export-ui.md:110-120
Implementation Details
Security: Formula Injection Escaping
The system automatically neutralizes potential CSV injection attacks. If a cell value starts with characters associated with spreadsheet formulas (=, +, -, @), it is prefixed with a single quote (').
// packages/core/src/lib/export-format.ts:27-35
export function escapeCsvValue(value: string): string {
if (typeof value !== 'string') return value;
// Neutralize common formula injection characters
if (value.startsWith('=') || value.startsWith('+') || value.startsWith('-') || value.startsWith('@')) {
return `'${value}`;
}
return value;
}
Sources: packages/core/src/lib/export-format.ts:27-35, packages/adapters/drizzle/tests/export-data.test.ts:74-85
Row Capping Policy
To ensure system stability, exports are bounded by a row cap. The system moved away from Number.MAX_SAFE_INTEGER to a documented default cap (e.g., 50,000 rows).
- Default Cap: Prevents OOM errors by limiting the result set size.
- Explicit Override: Consumers can pass a
maxRowsparameter to bypass the default limit if they accept the memory implications.
Sources: plans/050-export-ui.md:79-90, packages/adapters/drizzle/tests/export-data.test.ts:58-66
Adapter Contract
Adapters must implement the exportData method to support native server-side export. This method accepts ExportParams which include the current table state to ensure the exported file matches the user's view.
| Parameter | Type | Description |
|---|---|---|
format | 'csv' | 'json' | 'excel' | The requested output format. |
filters | FilterState[] | Active filters to apply to the export query. |
sorting | SortingState | Active sorting to apply to the export query. |
columns | string[] | Specific column IDs to include in the export. |
maxRows | number | Optional override for the row limit. |
Sources: packages/ui/tests/hooks/use-table-export.test.tsx:21-36, packages/adapters/drizzle/tests/export-data.test.ts:46-56
The csvExport Plugin
The csvExport plugin offers a lightweight alternative for client-side or post-fetch export logic. It captures the most recent fetch result using the afterFetch hook provided by the core factory.
Summary
The Data Export system in Better Tables ensures that users can reliably extract their data in CSV or JSON formats while maintaining the integrity of their filtered and sorted views. By combining a secure utility for formula escaping with a robust row-capping policy, the system provides a production-ready export solution that balances functionality with security and performance. Developers can trigger these exports either natively through database adapters or via the core csvExport plugin.