One-line import/export for every arc-cms admin table.
Drop one <script> tag into your CmsLayout and every admin page with a table instantly gets CSV, JSON, and XLSX import/export — with a 3-step preview wizard, upsert support, and a full audit history. Zero per-page code required.
Inspired by django-import-export.
- Auto-activates on every
<table>in the admin — no widget imports, no per-page code - ↓ Export to CSV, JSON, or XLSX in one click
- ↑ Import with a 3-step wizard: Upload → Preview (dry-run with error highlighting) → Confirm
- Upsert support — update existing records by a unique field, create new ones
- Batched commits — 500-row batches keep memory flat on large datasets
- Full history at
/admin/import-export— every import and export logged with row counts and errors - Lazy XLSX — SheetJS only loads when an XLSX request is actually made; CSV/JSON have zero runtime deps
- Dark mode native — uses your site's
--ui-*CSS variables automatically - Override-friendly — works on customized/overridden admin pages too
bun add @arc-language/arc-import-exportIn arc.config.json, add the server routes and schema:
{
"serverRoutes": [
"node_modules/@arc-language/arc-import-export/src/server/import-export.arc"
],
"schemas": [
"node_modules/@arc-language/arc-import-export/src/server/schemas/import-log.arc"
]
}In site/cms/CmsLayout.arc, add one line after your existing scripts:
@raw '<script src="/arc-ie/bar.js" defer></script>'
If you installed arc-cms via
arc cms init, this line is already there.
In your project root, create arc-import-export.config.js:
export default {
resources: {
users: { fields: ['name', 'email', 'role'], uniqueBy: 'email' },
pages: { fields: ['title', 'slug', 'published'], uniqueBy: 'slug' },
groups: { fields: ['name', 'slug', 'description'], uniqueBy: 'slug' },
}
}That's it. Open /admin/users — the Import/Export toolbar appears above the table.
/admin/users
┌─────────────────────────────────────────────────────┐
│ users ↓ Export ↑ Import History ↗ │ ← auto-injected toolbar
└─────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ Name Email Role Joined │
│ ... ... ... ... │ ← your existing table
└──────────────────────────────────────────────────────┘
Step 1 Upload → Step 2 Preview → Step 3 Confirm
──────────────── ───────────────────────── ────────────────
Drop file here ✓ 138 valid · ✗ 4 errors Import 138 records
CSV / JSON / XLSX [table of first 20 rows] Cancel
[error rows highlighted red]
The toolbar infers the resource from the URL path:
| URL | Resource |
|---|---|
/admin/users |
users |
/admin/my-orders |
my-orders |
/admin/products |
products |
To override (e.g. on a page with a custom URL), add a data-ie-resource attribute anywhere on the page:
@raw '<div data-ie-resource="products" data-ie-fields="name,sku,price"></div>'
Or use the optional widget:
import ImportExportBar from "@arc-language/arc-import-export/src/widgets/ImportExportBar.arc"
ImportExportBar resource="products" fields="name,sku,price" uniqueBy="sku"
// arc-import-export.config.js
export default {
resources: {
[resourceName]: {
fields: string[], // fields allowed in import (required for import to be enabled)
uniqueBy: string, // optional: upsert key — update if exists, create if not
}
}
}| Option | Type | Required | Description |
|---|---|---|---|
fields |
string[] |
Yes (for import) | Column names that can be imported. Export uses all columns by default. |
uniqueBy |
string |
No | Field used for upsert on import. If omitted, always creates new records. |
If a resource is not in the config, Export still works (shows all columns) but the Import button is hidden.
All routes are mounted automatically when you add the server route to arc.config.json.
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/arc-ie/bar.js |
Public | The auto-detect script (~3 KB gzip) |
GET |
/admin/api/ie/config |
editor+ | Field config for a resource |
POST |
/admin/api/ie/preview |
editor+ | Parse file, return preview (no DB write) |
POST |
/admin/api/ie/commit |
editor+ | Commit import to database |
GET |
/admin/api/ie/export |
editor+ | Stream export file download |
GET |
/admin/api/ie/logs |
editor+ | List import/export history |
GET |
/admin/api/ie/logs/:id |
editor+ | Single log detail with errors |
| Format | Import | Export | Notes |
|---|---|---|---|
| CSV | ✓ | ✓ | RFC 4180, handles quoted fields and escaped commas |
| JSON | ✓ | ✓ | Array of objects; rows/data wrapper accepted on import |
| XLSX | ✓ | ✓ | SheetJS (lazy-loaded — zero cost if unused) |
Every import and export is logged at /admin/import-export:
- Filter by action (import / export), status (success / partial / failed), or resource name
- Click any row to see full details including per-row error messages
- Counts: total rows, imported, skipped, errors
| Operation | Complexity | Notes |
|---|---|---|
| Preview parse | O(n) time, O(20) space | Only first 20 rows buffered for preview |
| Import commit | O(n) time, O(500) space | 500-row batch loop |
| Export (CSV/JSON) | O(n) time, O(1) space | Streaming response |
| Export (XLSX) | O(n) time, O(n) space | SheetJS buffer — unavoidable |
| Config fetch | O(1) | Cached 60 s per browser tab |
Contributions welcome. See CONTRIBUTING.md for guidelines.
# Clone
git clone https://github.com/arc-language/arc-import-export.git
cd arc-import-export
# Install
bun install
# Run against an arc-cms project (set ARC_PROJECT_DIR)
ARC_PROJECT_DIR=../my-arc-project bun run devMIT © KCuppens