useDataExport
Project an in-memory array of rows through a column definition and trigger a browser download as CSV, JSON, Markdown, or XLSX.
Installation
pnpm dlx @docyrus/cli add @docyrus/hooks-use-data-exportpnpm add xlsxxlsx is loaded with a dynamic import('xlsx') and is only fetched when format === 'xlsx'. CSV, JSON, and Markdown exports work without it.
Overview
useDataExport is a generic, framework-agnostic export hook for any in-memory array. You describe how each column should be projected, and the hook handles serialization, escaping, blob construction, and the browser download.
- Four formats —
csv,json,markdown, andxlsx. - Column projection — pick fields, rename headers, derive values via
accessor, format cells viaformatter. Order is preserved in the output. - Safe by default — CSV cells are RFC 4180 escaped, Markdown cells escape
|,\, and newlines, andDatevalues are written as ISO strings. - Excel-friendly — CSV output is prefixed with a UTF-8 BOM so Excel auto-detects encoding (toggle with
csvBom). - SSR-safe —
exportData()is a no-op on the server (typeof window === 'undefined') and returnsnullinstead of throwing. - Lazy xlsx — the
xlsxpackage is only loaded when the user actually picks the XLSX format.
Usage
Basic example
'use client';
import { Button } from '@docyrus/ui/primitives/ui/button';
import { useDataExport } from '@docyrus/ui/library/hooks/use-data-export';
type Contact = {
id: string;
name: string;
email: string;
createdAt: Date;
};
const rows: Array<Contact> = [
{ id: '1', name: 'Ada Lovelace', email: 'ada@example.com', createdAt: new Date() }
];
export function ExportContactsButton() {
const { exportData, isExporting } = useDataExport<Contact>({
fileName: 'contacts',
columns: [
{ id: 'id', header: 'ID' },
{ id: 'name', header: 'Full Name' },
{ id: 'email', header: 'Email' },
{ id: 'createdAt', header: 'Created' }
]
});
return (
<Button
disabled={isExporting}
onClick={() => exportData(rows, 'csv')}>
Export CSV
</Button>
);
}Custom accessor + formatter
const { exportData } = useDataExport<Contact>({
fileName: 'contacts',
columns: [
{
id: 'fullName',
header: 'Full Name',
accessor: (row) => `${row.firstName} ${row.lastName}`
},
{
id: 'amount',
header: 'Amount',
accessor: (row) => row.amountCents,
formatter: (value) => `$${(Number(value) / 100).toFixed(2)}`
},
{
id: 'tags',
header: 'Tags',
accessor: (row) => row.tags,
formatter: (value) => (value as Array<string>).join(', ')
}
]
});accessor runs first to pull the raw value off the row (defaults to row[id] when omitted). formatter runs second to coerce the raw value into the string written to the file. The default formatter renders primitives verbatim, ISO-formats Dates, and JSON.stringifys objects/arrays.
Picking a format at runtime
const formats: Array<DataExportFormat> = ['csv', 'json', 'markdown', 'xlsx'];
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button disabled={isExporting}>Export…</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
{formats.map((format) => (
<DropdownMenuItem key={format} onSelect={() => exportData(rows, format)}>
{format.toUpperCase()}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
);Awaiting the resolved file metadata
async function downloadAndToast() {
const result = await exportData(rows, 'xlsx');
if (!result) return; // server-side, no-op
toast.success(`Saved ${result.fileName}`);
}API Reference
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
options | UseDataExportOptions<TData> | — | Hook configuration (see below). Required. |
UseDataExportOptions<TData>
| Option | Type | Default | Description |
|---|---|---|---|
columns | Array<DataExportColumn<TData>> | — | Column projection. Order is preserved in the output. Required. |
fileName | string | 'export' | File name without extension. The format-specific extension is appended automatically. |
sheetName | string | 'Sheet1' | Sheet name used for xlsx exports. |
csvBom | boolean | true | Prepend a UTF-8 BOM to CSV output so Excel auto-detects encoding. |
DataExportColumn<TData>
| Property | Type | Default | Description |
|---|---|---|---|
id | string | — | Stable column id. Used as the JSON object key when format === 'json' and as the lookup key when accessor is omitted. Required. |
header | string | — | Header label rendered in CSV / Markdown / XLSX output. Required. |
accessor | (row: TData) => unknown | row => row[id] | Pull the raw cell value for this column from a row. |
formatter | (value: unknown, row: TData) => string | default stringifier | Coerce the raw value into the string written to the file. The default renders primitives verbatim, ISO-formats Dates, and JSON.stringifys objects/arrays. |
Return Value — UseDataExportResult<TData>
| Property | Type | Description |
|---|---|---|
exportData | (rows: Array<TData>, format: DataExportFormat) => Promise<DataExportResult | null> | Project rows through columns and trigger a browser download in the requested format. Returns null on the server. |
isExporting | boolean | true while a download is in flight (mainly relevant for the async xlsx path). Use it to disable the trigger button. |
DataExportResult
| Property | Type | Description |
|---|---|---|
mimeType | string | MIME type written to the Blob. |
extension | string | File extension including the leading dot (e.g. '.csv'). |
fileName | string | Final file name (<fileName><extension>). |
Type Exports
| Type | Description |
|---|---|
DataExportFormat | 'csv' | 'json' | 'markdown' | 'xlsx' |
DataExportColumn<TData> | Single-column definition (id, header, accessor, formatter). |
UseDataExportOptions<TData> | Hook input (columns, fileName, sheetName, csvBom). |
UseDataExportResult<TData> | Hook return value (exportData, isExporting). |
DataExportResult | File metadata returned by exportData(). |
Format Reference
| Format | Extension | MIME Type | Notes |
|---|---|---|---|
csv | .csv | text/csv;charset=utf-8 | RFC 4180 escaping. UTF-8 BOM prepended unless csvBom: false. CRLF line endings. |
json | .json | application/json;charset=utf-8 | Pretty-printed (JSON.stringify(rows, null, 2)). Object keys come from each column's id. |
markdown | .md | text/markdown;charset=utf-8 | GitHub-flavored table. |, \\, and newlines are escaped; multi-line cells use <br />. |
xlsx | .xlsx | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet | Lazy-loads the xlsx package. Numbers, booleans, and Date values are written as their native Excel types. |
Notes
- The hook keeps no internal cache — pass the rows you want to export each time you call
exportData(). This is what lets the same hook handle a filtered grid, a selection-only export, and a "download all" button. - Calling
exportData()from a server component or during SSR is safe: the function bails out before touchingdocumentand resolves tonull. - The download is triggered by injecting a temporary
<a download>element. No popup, no new tab, no user gesture beyond the original click.