Migrate from Shopware to Vendure: a partner-written guide | Vendure
Written by Kevin Mattutat from SpaceParrots
Shopware to Vendure
What moves from Shopware 6 to Vendure as data, what gets rebuilt as code, and the traps that never raise an error.
Compare Vendure and Shopware See pricing
Migration guide
Catalogue
1,514 variants
About a thousand demo products through the Admin API, checked value by value against Shopware's own APIs.
Order history
306 orders
Moved as finished records, not replayed. Every stored total equals its invoice, within a cent per discount share.
Passwords kept
151 of 151
Every registered customer logged into the Vendure Shop API with their Shopware password.
Rule prices
4 in 5
Variants whose Shopware price differs from the base price that moved. The share is demo data; the mechanism is not.
In short
Three parts, and they cost very different amounts
The catalogue moves as data through Vendure's Admin API. Customers and order history move as data too, but no API takes them, so a script running inside Vendure saves them. Everything Shopware calculates at runtime is rebuilt as code: rule prices, Rule Builder conditions, Flow Builder automations, Shopping Experiences layouts, and payment and shipping handlers. That third part is where the project cost sits.
Kevin, a Shopware extension developer, built an open-source migrator for this guide and ran it on Shopware 6.7's demo shop, so the figures and effort ranges come from that run: SpaceParrots/shopware-vendure-migrator.
Moving the data is the easy part. The challenge is thinking through the customisations and edge cases.
Is this guide for your shop?
A good fit
- Self-hosted or PaaS Shopware 6.7
- Your own developers, or an agency, writing the commerce logic
- Variants, several languages and tax rates
- Some price rules
Not a good fit
- Prices and merchandising run in the Rule Builder, with no developers
- Automation lives in the Flow Builder, pages in Shopping Experiences
- A Shopware major upgrade still to finish: two platform projects in one year
- B2B Components (Evolve and above) in use: employee accounts, roles, quotes and approvals are a project of their own, not a data migration. Vendure Core has none of them; the paid plans add them
Written for developers and tech leads judging whether a Shopware 6.7 shop can make the move, and what it takes. In Vendure each new rule or flow is code; a team without developers would trade tools it uses every day for work nobody on it can do.
Count before you map
| Count | Why it matters |
|---|---|
| Products with and without variants | Either way, each becomes one Vendure product with at least one variant |
| Variants that inherit from their parent | Every inherited field and association has to be resolved before loading |
| Rule prices, per rule and condition type | One piece of code per condition type |
| Customer rows per email, and guest rows | Shopware creates a row per guest checkout; Vendure keeps one customer per email |
| Order, delivery and payment state combinations | Each one that occurs needs a Vendure state |
| Order line items per type | Only product lines become Vendure order lines |
| Canonical SEO URLs | The redirect map is only as complete as this table |
| Shopping Experiences layouts | Each one is rebuilt in the storefront or a CMS |
| Settings overridden per sales channel | The shop runs on the overrides in system_config, not on the defaults |
Then give every plugin and integration a Vendure answer and an owner before the project starts, sorted by what it adds: data, behaviour, or a connection to another system.
The migration
The migrator in five stages
- Extract
Every table in one read-only transaction, draft versions skipped. - Transform
Resolve inheritance, merge customers by email, map each order's states. - Load
The catalogue through the Admin API; customers and orders through a script inside Vendure. - Verify
Vendure against the target model, and customers and orders against Shopware's database. - Compare
The catalogue against Shopware's own Admin and Store APIs.
Every stage can run again on its own, so the cutover can be rehearsed on a copy of production as often as needed. The loader creates and skips but does not yet update changed objects: a real cutover also needs updates and a delta run for the freeze window. After the last load, clean up the job queue and rebuild the search index. Appendix B has the code.
What the API cannot take needs no SQL
The catalogue goes through Vendure's Admin API. Customers and orders cannot: the API takes no password hash, creates no customer without a login, and has no order import, since a draft order reprices every line and takes a new number and today's date.
So a standalone script boots Vendure with bootstrapWorker() and the server's own config ( stand-alone scripts), and saves customers and orders as Vendure entities. Custom fields, plugins and price and tax strategies still apply, and nobody writes SQL against Vendure's tables.
Pictured: a migrated product with its "Colour" option group, three variants, and facet values that came from Shopware properties.
Why the script saves entities directly
The order service would reprice imported orders, and the customer service would hash a new password and publish a registration event. So the loader uses Vendure's services for customer groups and shipping methods, and saves orders and customers as entities through the database connection.
// Type-safe GraphQL via gql.tada
const productQuery = graphql(`
query Product($slug: String!) {
product(slug: $slug) {
id
name
priceWithTax
stockLevel
}
}
`)
// Server component — Next.js 16 RSC
export async function ProductCard({ slug }: { slug: string }) {
const { product } = await query(productQuery, { slug })
return (
<Card>
<CardTitle>{product.name}</CardTitle>
<Price value={product.priceWithTax} />
</Card>
)
}
Rebuild the storefront, ideally from a starter
Vendure Core has no built-in storefront, only an API the shop calls, so the Shopware storefront has to be rebuilt. The reference migration did not build one. From the Scale plan, the B2B storefront is included.
You can technically reuse the Twig templates from a PHP app, or move the Vue components into a Nuxt app, and point either at Vendure's API, but every data call still has to be rewritten. The faster way is one of Vendure's storefront starters in Next.js or TanStack Start, where the basic shop already works and you adapt the design.
What ported as data, and what got rebuilt
Ported or transformed as data
- Products, variants and option groups
- Properties and manufacturers, as facets
- Categories, as collections with the same tree and order
- Media, translations, base prices and stock
- Tax rates, regrouped into zones
- Customers with passwords, groups and addresses
- Orders as records: amounts, dates, states, payments, fulfilments
- Canonical SEO URLs, as a redirect map
Rebuilt, replaced or retired
- Rule prices: a price calculation strategy
- Rule Builder on shipping, payment and promotions: checkers and conditions
- Flow Builder automations: event subscribers
- Shopping Experiences layouts: storefront or CMS
- Product streams, and the "link only" and "search only" visibility levels
- Payment and shipping handlers, mail templates, number ranges
- Partial shipments and refunds, returns, invoices: kept as an archive
The right column is also what merchandisers lose as self-service. In Vendure a developer writes the condition once; merchandisers then configure promotions, shipping and payment from what exists.
Trap 1 of 4 · none raised an error
Variants inherit whole associations, not just fields
A variant inherits its tax rate from its parent, and its categories and properties as a whole set. In the reference shop, not one variant had its own.
Rules, in the order Shopware checks them priority DESC, id ASC
- P100 0a…Billing country is USDoes not match
- P100 3f…Cart amount above thresholdMatches, no prices here
- P100 8c…All customersFirst with prices: wins
- P10 1b…Day of week is SundayCheaper, never reached
Within the winning rule, the tier covering the line quantity applies. No matching rule with prices means the base price.
Why developers move
Configuration in Shopware, code in Vendure
Kevin has built Shopware extensions since early version 6, but never run a shop. Shopware lets a merchant change prices, shipping, payment and automations without a developer, in the Rule Builder and Flow Builder. Custom work is harder: it runs through Shopware's own data abstraction layer, with its limits, and every storefront component also has to work as a Shopping Experiences block.
Vendure makes the opposite trade: the team builds the shop around its own business model, in code. That suits a model no standard shop fits, such as a marketplace.
- The business logic is yours. Every rule Shopware offers as a no-code tool is a named TypeScript extension point in Vendure: price calculation, tax, shipping eligibility, promotion conditions, the order process ( strategies).
- Headless, so the parts scale on their own. The same API serves a web shop, an app or a point of sale, and the backend scales independently of the storefront.
Whether that trade is worth a migration is the merchant's decision. This is the code side of it.
Rehearse locally, cut over from a dump
The reference migration has not gone live and ran on one machine. For a real shop, Kevin would not open both production databases. Rehearse locally instead: restore a Shopware dump, run a local Vendure with the production configuration and version, and verify. For the final run, put Shopware into maintenance mode, take a fresh dump, let the local Vendure write its assets straight to the production S3 bucket, then restore the Vendure database dump on the server. Rehearse into a separate bucket, or every run leaves a full set of images behind.
Six risks to check
Stale source indexes
Run bin/console dal:refresh:index twice before the final extract, and check it. In the demo shop the listing index was stale and almost no product had an SEO URL.
Redirects
Build them from Shopware's canonical SEO URLs, after the index rebuild.
Background jobs
Measure the queue in a rehearsal, not on go-live day.
Prices
If rule prices are simplified instead of rebuilt, announce every price change before go-live.
Customer logins
Passwords carry over. Accounts bound to different sales channels can share an email: decide which one Vendure keeps. Older legacy_password hashes need a reset.
Order history
Imported orders reserve no stock and send no mail. Refunds need a payment handler with the old method's code. Keep the invoice PDFs as the legal record.