WooCommerce to Shopify Product Import: CSV, Migration App, or Custom Script
A bare Shopify CSV import will take your WooCommerce products, but it silently drops anything past 3 variant options, leaves WordPress shortcodes sitting raw in your descriptions, and can re-issue every variant ID the moment you re-import — breaking any subscription or loyalty app keyed to the old ones. Here's what each of the three real migration paths actually preserves, and which one fits a catalog like yours.
A plain Shopify CSV import will move your WooCommerce product titles, prices, and descriptions, but it will not preserve everything, and which gaps matter depends on your catalog: products with more than three attributes lose the extras entirely, HTML from WordPress page builders often renders as broken code instead of formatted text, and images can drop out completely if your spreadsheet tool re-sorted the file before you uploaded it. There are three real ways to move a WooCommerce catalog to Shopify — a bare CSV import, a paid migration app, or a custom script against the Admin API — and each one preserves a different slice of your data. Picking the wrong one is how a "successful" migration turns into three weeks of manually rebuilding product pages after the fact.
This isn't a "steps to import a CSV" walkthrough — Shopify's own help center already covers that. It's the part that guide leaves out: what actually breaks in each method, why, and who should pick which one.
What a bare CSV import actually does and doesn't preserve
Shopify's product CSV import is free, built into every plan, and genuinely fine for a small, simple catalog. It also has real, documented limits that most "how to migrate" posts skip past:
- Three options, no more. Shopify's own migration documentation states it plainly: "Shopify only allows for 3 product options. Products with greater than 3 options won't have their options imported." A WooCommerce variable product with size, color, material, and finish will land on Shopify with one of those four silently missing, and nothing in the import summary flags which products it happened to.
- WordPress shortcodes don't translate. WooCommerce's short and long description fields frequently contain shortcodes from Elementor, Divi, or WooCommerce's own layout blocks —
[vc_row],[/tab], that sort of thing. Shopify's CSVBody (HTML)column has no idea what a WordPress shortcode is; it imports the literal text, and your product page shows raw bracketed code sitting in the middle of the copy instead of the formatting it used to produce. - Images depend on file hygiene you probably didn't think to check. Shopify's own documentation for the CSV format specifically warns that re-sorting the import file in Excel or Numbers can detach an image's row from the product row it belongs to, because the two are linked purely by row adjacency and a matching handle — not an explicit foreign key. Open the CSV in a spreadsheet app, sort a column to tidy it up, save, and you can silently reassign images to the wrong products or drop them.
- Anything outside core product fields has no home. WooCommerce custom fields, ACF fields, and product reviews aren't part of the standard product CSV at all. Shopify's migration guide mentions this only once, in passing, as a note that data like this "can be addressed via 3rd party apps or metafields" — which means you have to plan a separate metafield structure and a separate import before any of that data has anywhere to go.
- 15MB per file. A large catalog with dense variant data will hit this and need splitting into multiple CSVs, which is mechanical but easy to forget until the upload fails partway through a migration weekend.
None of this makes CSV import a bad choice. For a catalog under a few hundred simple products with three or fewer real variant dimensions and descriptions that are already plain HTML, it's the fastest and cheapest path, and it's the one Shopify itself recommends first.
The variant ID trap almost nobody mentions
This is the gap that decides whether CSV import is safe for a store that already has other apps wired into it, and it's not in Shopify's migration guide at all — it's buried in the CSV reference documentation instead: "Changing data in the Option1 value, Option2 value, or Option3 value columns of the product CSV file deletes existing variant IDs, and creates new variant IDs." Shopify's own docs go on to warn that this can break third-party dependencies on those IDs.
In practice, that means if you import once, then re-import a corrected file with any variant option value edited — a typo fix in a color name, a renamed size label — every variant on that product gets a new ID, even though nothing about the product looks different to a shopper. If you have a subscription app, a loyalty program, a personalization tool, or any custom code that stored the old variant ID to remember what a customer bought, that reference is now pointing at nothing. This is exactly the kind of failure that shows up two weeks after go-live as "why did this customer's subscription stop renewing," not on migration day itself. If you expect to re-import corrected data more than once — and almost every migration does — treat your first CSV as a dry run on a duplicate development store, not the final import.
Migration apps: Matrixify, LitExtension, and Cart2Cart
Dedicated migration apps exist specifically to close the gaps above. They typically map WooCommerce's variable-product structure into Shopify's variant model more intelligently — collapsing extra attributes into metafields instead of dropping them, stripping or converting known page-builder shortcodes, pulling images by direct URL rather than relying on spreadsheet row order, and in some cases carrying over customer accounts, order history, and reviews that a plain product CSV was never going to touch.
What you're paying for is scope and reliability, not magic: no app can invent variant option slots Shopify's data model doesn't have, so a genuinely 4-attribute product still needs metafields or a bundling app on the Shopify side regardless of which migration tool moved it. And because these tools run their own transformation logic, you should still spot-check a sample of complex products after the run — a shortcode a tool doesn't recognize, or a WooCommerce attribute type it wasn't built to expect, can still slip through untouched. Budget for this route when your catalog has real complexity: heavy use of page builders in descriptions, products near or over the 3-option ceiling, or existing customer/order history you actually need on the new store rather than starting that ledger fresh.
A custom script against the Admin API
For a catalog large enough, or unusual enough, that neither a bare CSV nor an off-the-shelf migration app maps it cleanly, the third option is writing directly against Shopify's GraphQL Admin API. This is more work up front, but it's the only path that gives you full control over exactly how WooCommerce data becomes Shopify data, field by field.
The pieces you'd actually use:
productSet— a single mutation that creates or updates a product along with its variants and options in one call, which is the modern replacement for juggling separate product-then-variant mutations and is built for exactly this kind of sync-from-an-external-source job.bulkOperationRunMutation— for catalogs too large to push product-by-product inside normal rate limits, you upload a JSONL file (up to 100MB) and Shopify runs your mutation once per line asynchronously, which is the documented pattern for bulk importing through the GraphQL Admin API rather than looping single REST calls yourself.metafieldsSet— for every piece of WooCommerce data that doesn't have a native Shopify field: custom fields, ACF data, the fourth-and-beyond product attribute you can't fit into Option1–3. You define the metafield definitions first, then write to them in the same pass as the product itself, instead of bolting them on afterward.urlRedirectCreate— WooCommerce's default permalink structure (/product/product-name/) doesn't match Shopify's (/products/product-name). Generating this redirect map programmatically from the same source data you're already migrating is far less error-prone than hand-building it afterward from a spreadsheet, and it's the difference between a migration that keeps your existing search rankings and one that hands Google a wall of 404s the week you cut over.
This route makes sense when you have engineering time available and a catalog where the mapping logic itself is the hard part — heavily attributed B2B catalogs, products syncing from a PIM or ERP that WooCommerce was never the real source of truth for, or a migration where you need the import to be re-runnable idempotently without triggering the variant ID churn described above. It is not the right choice for a 200-SKU store on a deadline; that's what the first two options are for.
Which one fits your catalog
- Bare CSV import — small, simple catalogs, three or fewer real attributes, clean HTML descriptions, no other apps keyed to variant identity. Free, fast, and you should still treat the first run as a rehearsal on a development store before touching production.
- A migration app — the right default for most stores with page-builder-heavy descriptions, products near the 3-option ceiling, or order/customer/review history worth carrying over. Budget for licensing cost and a manual spot-check pass afterward.
- A custom script — large or structurally unusual catalogs, an external system of record you need to keep syncing after go-live, or a migration that has to be safely re-runnable. Requires engineering time, but it's the only option where you control every mapping decision directly.
Whichever path you pick, do the redirect mapping before cutover, not after. Shopify's own migration guide recommends setting up URL redirects in advance for pages customers might have bookmarked or that other sites link to — and a redirect built from the same mapping you used to move the products is far more reliable than one reconstructed from memory once the old store is already gone. It's also worth revisiting why a WooCommerce store outgrows its platform in the first place before you commit engineering time to a migration path — the reasons that push a catalog toward Shopify usually point at which of these three methods is worth the investment.
The short version
A bare CSV import is free and fine for a simple catalog, but it silently drops a fourth product option, leaves page-builder shortcodes in your descriptions, can misalign images if the file gets re-sorted, and will reissue variant IDs — breaking any app that stored the old ones — the moment you correct and re-import it. A migration app closes most of those gaps for a licensing fee and is the right default for anything with real complexity. A custom script against the Admin API is more work but the only option that gives you full control over the mapping, and it's worth it when your catalog is large, structurally unusual, or needs to stay synced with an external system after go-live.
If you're weighing which of these three actually fits your catalog, or want a second pair of eyes on the redirect map before you cut over, get in touch — I've done this migration enough times to tell you in ten minutes which path is worth your time. For the broader "should we even move off WooCommerce" question, our full WooCommerce and Magento migration guide covers the decision itself, not just the product import.