Export collection data to CSV and JSON formats for reporting, backups, and data migration.
Status: Stable
The export feature provides a composable and a ready-to-use button component for exporting collection data. It works with useCollectionQuery for client-side data or can fetch directly from the API for server-filtered exports.
Features:
<script setup lang="ts">
const { items } = await useCollectionQuery('products')
</script>
<template>
<CroutonExportButton
collection="products"
:rows="items"
/>
</template>
<script setup lang="ts">
const { items } = await useCollectionQuery('products')
const { exportCSV, exportJSON } = useCollectionExport('products')
</script>
<template>
<div class="flex gap-2">
<UButton @click="exportCSV(items)">Export CSV</UButton>
<UButton @click="exportJSON(items)">Export JSON</UButton>
</div>
</template>
useCollectionExport(collection)interface UseCollectionExportReturn {
exportCSV: (rows: any[], options?: ExportOptions) => void
exportJSON: (rows: any[], options?: ExportOptions) => void
exportWithQuery: (
format: 'csv' | 'json',
query?: Record<string, any>,
options?: ExportOptions
) => Promise<void>
isExporting: Ref<boolean>
}
interface ExportOptions {
// Field selection
fields?: (string | ExportField)[]
excludeFields?: string[]
// What to include
includeId?: boolean // default: false
includeMetadata?: boolean // default: false (createdAt, updatedAt, etc.)
// Transformations
transformRow?: (row: any) => any
// Output
filename?: string // default: collection name
dateFormat?: 'iso' | 'locale' | 'timestamp'
flattenNested?: boolean // Stringify nested objects
}
interface ExportField {
key: string
label?: string
transform?: (value: any, row: any) => any
}
const { exportCSV } = useCollectionExport('products')
exportCSV(items.value, {
fields: [
{ key: 'name', label: 'Product Name' },
{ key: 'price', label: 'Price ($)', transform: (v) => v.toFixed(2) },
{ key: 'category', label: 'Category' }
],
filename: 'product-report'
})
const { exportCSV } = useCollectionExport('orders')
exportCSV(orders.value, {
transformRow: (row) => ({
...row,
customerName: row.customer?.name,
customerEmail: row.customer?.email,
items: row.items?.map(i => i.name).join(', ')
}),
excludeFields: ['customer'] // Exclude original nested object
})
const searchQuery = ref('')
const statusFilter = ref('active')
const query = computed(() => ({
search: searchQuery.value,
status: statusFilter.value
}))
const { items } = await useCollectionQuery('products', { query })
const { exportWithQuery, isExporting } = useCollectionExport('products')
// Export ALL data matching filters (not just current page)
async function handleExport() {
await exportWithQuery('csv', query.value, {
filename: `products-${statusFilter.value}`
})
}
const { exportCSV } = useCollectionExport('products')
exportCSV(items.value, {
includeId: true,
includeMetadata: true, // Includes createdAt, updatedAt, etc.
dateFormat: 'locale' // Format dates for local timezone
})
CroutonExportButton<CroutonExportButton
collection="products"
:rows="items"
:formats="['csv', 'json']"
:options="{ excludeFields: ['internalCode'] }"
variant="ghost"
size="sm"
@export="handleExport"
@error="handleError"
/>
| Prop | Type | Default | Description |
|---|---|---|---|
collection | string | required | Collection name |
rows | any[] | required | Data to export |
formats | ('csv' | 'json')[] | ['csv', 'json'] | Available formats |
options | ExportOptions | {} | Export options |
variant | string | 'ghost' | Button variant |
size | string | 'sm' | Button size |
color | string | - | Button color |
disabled | boolean | false | Disable button |
| Event | Payload | Description |
|---|---|---|
export | [format, rows] | Fired on successful export |
error | [Error] | Fired on export error |
<CroutonExportButton collection="products" :rows="items">
Download Report
</CroutonExportButton>
<script setup lang="ts">
const { items } = await useCollectionQuery('products')
</script>
<template>
<CroutonCollection :rows="items" collection="products">
<template #header>
<div class="flex justify-between items-center p-4">
<h2 class="text-lg font-semibold">Products</h2>
<div class="flex gap-2">
<CroutonExportButton
collection="products"
:rows="items"
:options="{ excludeFields: ['teamId'] }"
/>
<UButton @click="open('create', 'products')">
Add Product
</UButton>
</div>
</div>
</template>
</CroutonCollection>
</template>
<script setup lang="ts">
const { items } = await useCollectionQuery('products')
const { exportCSV } = useCollectionExport('products')
const selectedRows = ref<string[]>([])
function exportSelected() {
const selected = items.value.filter(item =>
selectedRows.value.includes(item.id)
)
exportCSV(selected, { filename: 'selected-products' })
}
</script>
The following fields are excluded by default:
| Field | Reason | Override |
|---|---|---|
teamId | Internal identifier | N/A (always excluded) |
id | Often not needed in exports | includeId: true |
createdAt | Metadata | includeMetadata: true |
updatedAt | Metadata | includeMetadata: true |
createdBy | Metadata | includeMetadata: true |
updatedBy | Metadata | includeMetadata: true |
The export handles special characters properly:
""The component uses translation keys for labels:
| Key | Default |
|---|---|
export.button | Export |
export.csv | Export as CSV |
export.json | Export as JSON |
export.noData | No data to export |
export.success | Export completed |
export.error | Export failed |
exportWithQuery (configurable server-side)