strapi-plugin-data-excel-exporter
v1.1.1
Published
Export Strapi v5 collections to xlsx files from the admin panel.
Maintainers
Readme
strapi-plugin-data-excel-exporter
Export Strapi v5 collection entries to .xlsx files directly from the admin panel.
Select one or more entries in any Content Manager list view; an Export action appears alongside Delete/Publish. Click it and the selected entries stream down to your browser as an Excel workbook with every flattenable field included.
Requirements
- Strapi
^5.0.0 - Node
>=20.0.0 <=24.x.x - React 18
Installation
yarn add strapi-plugin-data-excel-exporter
# or
npm install strapi-plugin-data-excel-exporterEnable the plugin in config/plugins.ts (or .js):
export default () => ({
'data-exporter': {
enabled: true,
},
});Then rebuild and start the admin:
yarn build
yarn developPermissions
Bulk-exporting a collection is intentionally separate from reading individual entries. After installing the plugin, grant the Export collections action to roles that should have it:
- Open Settings → Administration Panel → Roles.
- Pick a role (e.g. Editor).
- Under the relevant content type, tick Export collections.
- Save.
Users without this permission will see a 403 Forbidden toast when they try to export.
Super-admins always have access — they don't need explicit permission.
Usage
- Open any collection in the Content Manager.
- Tick the checkboxes for the rows you want to export.
- Click Export in the bulk-action bar that appears above the table.
- The
.xlsxdownloads automatically with every flattenable field included for the selected entries.
To export the entire collection, use the "select all" checkbox in the table header (Strapi-managed, paginates as expected).
Configuration
All keys are optional; defaults are shown.
// config/plugins.ts
export default () => ({
'data-exporter': {
enabled: true,
config: {
maxRows: 50_000,
pageSize: 500,
flattenDepth: 3,
relationDisplayField: 'id',
excludeAttributes: ['password', 'resetPasswordToken'],
filenameTemplate: '{collection}-{date}.xlsx',
sanitizeFormulas: true,
},
},
});| Key | Default | Description |
|---|---|---|
| maxRows | 50000 | Hard cap on entries per export. Exports above this fail with HTTP 413. |
| pageSize | 500 | Internal pagination size used while streaming. Auto-clamped to maxRows. |
| flattenDepth | 3 | How many levels of nested components to flatten into dotted columns. Deeper paths are collapsed to a JSON column. Range [1, 10]. |
| relationDisplayField | 'id' | Which field of a related entry to render in cells. Many-relations are joined by comma. |
| excludeAttributes | ['password', 'resetPasswordToken'] | Attribute names that are never exported, regardless of selection. |
| filenameTemplate | '{collection}-{date}.xlsx' | {collection} → info.singularName. {date} → ISO-8601 basic UTC (e.g. 20260427T143015Z). |
| sanitizeFormulas | true | If true, prepend ' to cell values starting with =/+/-/@. See Security below. |
How an export happens
admin click → POST /data-exporter/export
{ uid, query: { filters: { documentId: { $in: [...] } } } }
→ isAuthenticatedAdmin policy
→ has-export-permission policy (RBAC check on uid)
→ controller validates body
→ exporter.run
├─ describe(uid) → resolve flattenable fields
├─ documentService.count(query) → check vs maxRows
├─ open xlsx stream-writer
└─ paginate documentService.findMany (page=N, pageSize)
└─ per entry: flatten → xlsx-writer.appendRow
├─ commit
└─ return Readable
→ controller pipes Readable to response
→ browser downloads <collection>-<ISO>.xlsxThe export streams — entries flow from the database, through pagination, into xlsx rows, and out to the browser without ever buffering the full file in memory.
The endpoint also accepts an optional fields: string[] to restrict columns; the bulk-action UI omits it (sends every flattenable field).
What gets exported
| Strapi attribute | Exported as |
|---|---|
| Scalars (string, integer, boolean, date, datetime, JSON, enumeration, …) | One column each |
| Components (single, nested) | Flattened into dotted columns (address.country.name) up to flattenDepth |
| Repeatable components | One column, JSON-encoded array |
| Dynamic zones | One column, JSON-encoded array |
| Media (single) | URL string |
| Relations (single) | The related entry's relationDisplayField (default: id) |
| Relations (many) | Comma-joined relationDisplayField values |
| Password | Always excluded |
Security: formula injection
By default (sanitizeFormulas: true), any cell value starting with =, +, -, or @ gets a leading apostrophe (') prepended. This neutralizes the well-known CSV/xlsx formula-injection vector — without it, a user who can write into a content-type field could craft a value like =HYPERLINK(...) that executes when another admin opens the exported file in Excel.
Disable only if your data is fully trusted and downstream consumers handle escaping themselves.
Limits
- Row cap: 50,000 by default. Tune via
maxRows. Exceeding it fails the export with HTTP 413 (ROW_LIMIT_EXCEEDED) before any bytes are written. - Request timeout: For exports near the cap (~30–60s on commodity hardware with wide schemas), make sure your Strapi/Koa
requestTimeoutis at least 120s. Adjust inconfig/server.ts:export default ({ env }) => ({ // … requestTimeout: 120_000, });
FAQ
Q: Can I export every locale at once? No — Strapi's bulk-selection lives within the current locale. Switch the locale picker to export a different language, or repeat per locale.
Q: Can I customize the relation display per content type?
Not in v1. relationDisplayField is global. Per-relation overrides are tracked for v2.
Q: Can I pick which columns to include?
Not from the bulk action — it always exports every flattenable field. The HTTP endpoint (POST /data-exporter/export) accepts an optional fields: string[] if you want to drive it programmatically.
Q: Why is the file empty / why does my browser open the response inline?
Some networks rewrite Content-Disposition headers. If filenames look generic (export.xlsx) or the file streams to the browser tab instead of downloading, check that no proxy is stripping headers between Strapi and the client.
Q: My collection has 200k rows. How do I export it?
Increase maxRows and requestTimeout (and your Node memory if needed). For collections that large, an async/job-queue mode is on the roadmap for v2.
Out of scope (v1)
- REST or CLI export triggers
- Bulk-action on selected rows
- Async/job-queue mode
- Multi-sheet "full fidelity" workbooks
- Per-relation
relationDisplayFieldoverrides - Strapi v4 compatibility
License
MIT — see LICENSE.
