name: product-import-export-plugin title: Product Import & Export description: Vendure plugin for importing and exporting product data via CSV. version: 3.6.4 tags:
A Vendure plugin for importing and exporting product data via CSV. Supports bulk product management and data migration between environments.

Vendure ^3.6.0
Add the plugin to your Vendure configuration.
| Option | Type | Default | Description |
|---|---|---|---|
updateProductSlug | boolean | true | Update product slug from the name field in the CSV |
restoreSoftDeleted | boolean | true | Restore soft-deleted products/variants found by id or SKU during import. If false, new products will be created instead. |
storageStrategy | ImportJobStorageStrategy | local disk | Strategy instance for queued import file storage and retrieval. |
storageStrategyFactory | (injector: Injector) => ImportJobStorageStrategy | undefined | Factory for DI-aware strategy creation (recommended for S3/remote storage). |
importJobStorage | 'local' | 's3' | 'local' | Deprecated compatibility flag. Prefer storageStrategy / storageStrategyFactory. |
Available import storage strategies:
LocalImportJobStorageStrategy (default)S3ImportJobStorageStrategy| Option | Type | Default | Description |
|---|---|---|---|
defaultFileName | string | 'products_export.csv' | Default filename for exports |
exportAssetsAsOptions | ('url' | 'json')[] | ['url', 'json'] | Available formats for asset export |
defaultExportAssetsAs | 'url' | 'json' | 'url' | Default asset export format |
defaultExportFields | string[] | See above | Fields that will be pre-selected in the export UI |
requiredExportFields | string[] | ['name', 'sku'] | Fields that must always be included |
customExportColumns | CustomExportColumn[] | undefined | Extra CSV columns resolved per variant row. See Custom export columns below. |
storageStrategy | 's3' | 'disk' | 'disk' | Where exported files are stored |
s3Options | object | undefined | S3 configuration (required when storageStrategy is 's3') |
The plugin supports configurable storage for exported files:
Add columns to the exported CSV whose value is computed per variant row, for example a permalink built from the product slug and variant SKU.
name – Must be non-empty, must not contain : (colon-suffixed headers are reserved for translated columns), must not collide with a built-in export field name, and must be unique among customExportColumns entries. These rules are validated when the plugin is initialised, so a misconfigured column fails fast at startup rather than mid-export.onExportStart – Optional. Awaited once per export job, before the first resolve call.resolve(ctx, injector, product, variant) – Called once per exported variant row. Its return value is written to the cell (String(value), or an empty cell for null/undefined). Use injector to pull in Vendure services via DI.name is included in the selected export fields, both in the header row and in the request; otherwise it is skipped entirely.resolve throws, the plugin logs a warning and writes an empty cell for that row instead of failing the whole export.name – Product name (use name:en, name:sv etc. for multiple languages)sku – Variant SKU (unique per variant)| Column | Format | Example |
|---|---|---|
name, slug, description | Text, or name:en, name:sv for translations | My Product |
assets | Pipe-separated URLs, or JSON [{"url":"...","name":"..."}] | https://example.com/1.jpg|https://example.com/2.jpg |
facets | facet:value pairs, pipe-separated | Brand:Acme|Category:Electronics |
optionGroups | Pipe-separated group names. Use name:code to share a group across products (see Sharing option groups) | Size|Color or size:shoe-size |
optionValues | Pipe-separated values (order matches option groups) | Large|Blue |
price | Number (in smallest currency unit) | 9999 |
taxCategory | Tax category name | standard |
stockOnHand | Number | 100 |
trackInventory | true or false | true |
variantAssets | Same format as assets | https://example.com/variant.jpg |
variantFacets | Same format as facets | Material:Cotton |
enabled | true or false | true |
| Custom fields | Column name = custom field name; value depends on field type | Add columns for any Product or ProductVariant custom fields defined in your Vendure config. Relation custom fields export the related entity's id (id|id for list fields), including fields that relate a product or variant to its own entity type. |
optionGroups and optionValues are required.400 with { "errors": ["Invalid custom field: product:clubName"] }, the Dashboard lists the errors, and nothing is imported.slug, description, assets, facets, optionGroups and product: custom fields) is read from a product's first row, the row with a name. If a later variant row of the product fills one of these columns, that product is not imported and the import reports the column and line. Other products in the file still import. id may repeat on every row.A relation custom field to ProductVariant can name the related variant by SKU instead of id. Add :sku to the column header after the type:
SKU|SKU.400 with { "errors": ["Row 2: no variant with SKU BASE-TEE-M"] }, the Dashboard lists the rows, and nothing is imported. The import job checks again when it runs.:sku is the only lookup, and it works only on relation fields to ProductVariant. Columns without it still take ids.By default, each product gets its own option groups. To share an option group across
multiple products (e.g. a common "Size" group for all shoes), use the name:code syntax in the
optionGroups column: size:shoe-size. All products that reference the same code will share
a single option group. Products using a plain name (e.g. just size) will each get their own
independent option group.
The first product to reference a given code defines the full set of option values for that
shared group; subsequent products may use any subset of those values. Codes are language-
agnostic — only the name portion before the colon is translated per language column
(optionGroups:en, optionGroups:sv, etc.).
Use language-specific columns by suffixing the field name with the language code. The language codes must match those configured in your Vendure instance (e.g. en, sv).
| Column pattern | Example columns | Use case |
|---|---|---|
name:en, name:sv | Product name per lang | Translated product names |
slug:en, slug:sv | Slug per lang | Translated URLs |
description:en | Description per lang | Translated descriptions |
facets:en, facets:sv | Facets per lang | Translated facet names and values |
optionGroups:en | Option groups per lang | Translated option group names (e.g. Size, Color) |
optionValues:en | Option values per lang | Translated option values (e.g. Large, Blue) |
Example CSV with English and Swedish:
Fields that can have multiple values use the pipe character | as a separator:
Brand:Acme|Category:ElectronicsSize|ColorLarge|Blue (order matches option groups)https://example.com/1.jpg|https://example.com/2.jpg:::note The Admin UI offers basic import and export. Some features such as the export-all option and the list of exported files are only available in the Dashboard. New features will be added to the Dashboard only. :::
optionGroups and optionValues must be included.id (the Vendure product id) is available as a selectable export field. It is written after the translated name/slug/description columns, so it is never CSV column 0, since import treats column 0 positionally as a product id for by-id matching.customExportColumns is configured, only the columns whose name is selected are written to the CSV.productId is not available as a selectable export field.customFields are not selected, no custom-field columns are written to the CSV.name through facets) and before optionGroups, which is the last product column so it sits next to optionValues, the first variant column. Product custom fields are filled on a product's first row only. Import reads product data from that row, so edit product fields there.
If you use @vendure/email-plugin, you can add an email notification when an export finishes from the /email subpath.
The plugin now provides a default fallback template for product-export-complete/body.hbs.
If you define your own template in your app's template directory, your template takes precedence.
Note: templates for @vendure/email-plugin should be valid MJML.
This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.