How to Migrate Your WooCommerce Store to a Headless Frontend (Step-by-Step)
Moving a WooCommerce store to a headless frontend is one of the most impactful architectural changes you can make. It separates the rendering layer from WordPress entirely, giving you sub-second page loads, full control over your frontend code, and a deployment workflow that belongs in 2025 — not 2015.
This guide walks through every step of the process. Not theory. Not vague advice. Actual steps, in order, with the configuration details and gotchas that other guides leave out. If you have already read our introduction to headless WooCommerce, you understand the "why." This is the "how."
TL;DR
Prerequisites: what you need before starting
Before touching any code, make sure these are in place. Missing any of them will stall you mid-migration.
- A working WooCommerce store on WordPress 6.0+ with WooCommerce 8.0+
- Node.js 20+ installed locally (use nvm to manage versions)
- A hosting provider that supports Node.js deployments (Vercel, Netlify, or a VPS)
- Basic familiarity with JavaScript, React, and the command line
- SSH or WP-CLI access to your WordPress server for configuration
- A staging domain or subdomain for parallel running (e.g. staging.yourstore.com)
You do not need to be a React expert. If you can read JSX and understand component props, you can build a headless storefront. The WooCommerce REST API handles the heavy lifting — your frontend is primarily rendering data it receives as JSON.
Do not skip the staging step
Step 1: Configure your WordPress backend
Your WordPress installation needs to become an API-first backend. This means enabling the REST API, configuring authentication, and setting up CORS so your frontend can fetch data from a different domain.
Enable and verify the WooCommerce REST API
The WooCommerce REST API is enabled by default, but you need to generate API keys. Go to WooCommerce → Settings → Advanced → REST API and create a new key pair. Set the permissions to Read/Write — you will need write access for cart and checkout operations. Store the Consumer Key and Consumer Secret securely. These go into your Next.js environment variables.
Verify the API is responding by hitting your store's endpoint directly: https://yourstore.com/wp-json/wc/v3/products. You should see a JSON response with your product data. If you get a 404, check that pretty permalinks are enabled in Settings → Permalinks — the REST API requires them.
REST API or WPGraphQL?
Configure CORS headers
Your Next.js frontend will run on a different domain (or port during development). Without CORS headers, the browser will block API requests. Add this to your theme's functions.php or a custom plugin:
add_action('rest_api_init', function() {
remove_filter('rest_pre_serve_request', 'rest_send_cors_headers');
add_filter('rest_pre_serve_request', function($value) {
$origin = get_http_origin();
$allowed = ['https://yourstore.com', 'http://localhost:3000'];
if (in_array($origin, $allowed)) {
header('Access-Control-Allow-Origin: ' . $origin);
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Authorization, Content-Type');
}
return $value;
});
});Replace the $allowed array with your actual domains. During development, http://localhost:3000 is essential. In production, use your headless frontend's domain.
Authentication for cart and checkout
Product data can be fetched without authentication (it is public). But cart operations, customer data, and order creation require authenticated requests. For server-to-server calls from Next.js API routes, use the Consumer Key and Secret as query parameters or via HTTP Basic Auth. For client-side cart operations, you will need a session-based approach — more on this in Step 4.
- Generate WooCommerce REST API keys with Read/Write permissions
- Verify the API responds at /wp-json/wc/v3/products
- Add CORS headers allowing your frontend domain and localhost
- Enable pretty permalinks (required for REST API)
- Install and configure JWT Authentication plugin if using client-side auth
- Disable the WordPress frontend theme (optional — saves resources)
Step 2: Set up your Next.js project
With the backend configured, create your Next.js application. This will be the new frontend for your store — every product page, category listing, cart, and checkout will be rendered here.
npx create-next-app@latest your-store --typescript --tailwind --app
cd your-storeCreate a .env.local file in the project root with your WooCommerce credentials:
NEXT_PUBLIC_WORDPRESS_URL=https://yourstore.com
WC_CONSUMER_KEY=ck_your_consumer_key
WC_CONSUMER_SECRET=cs_your_consumer_secretNever expose secrets to the client
NEXT_PUBLIC_ are available in client-side code. Your WooCommerce Consumer Key and Secret must not have this prefix. They should only be used in server components, API routes, and server actions — never in client components.Create a WooCommerce API client
Build a reusable API client that handles authentication and error handling. This is the foundation every page and component will use to fetch data from your WordPress backend:
// lib/woocommerce.ts
const baseUrl = process.env.NEXT_PUBLIC_WORDPRESS_URL + '/wp-json/wc/v3';
export async function wooFetch(endpoint: string, options?: RequestInit) {
const url = new URL(baseUrl + endpoint);
url.searchParams.set('consumer_key', process.env.WC_CONSUMER_KEY!);
url.searchParams.set('consumer_secret', process.env.WC_CONSUMER_SECRET!);
const res = await fetch(url.toString(), {
...options,
next: { revalidate: 60 }, // ISR: revalidate every 60 seconds
});
if (!res.ok) throw new Error(`WooCommerce API error: ${res.status}`);
return res.json();
}This client uses Next.js Incremental Static Regeneration (ISR) by default, meaning product data is cached and revalidated every 60 seconds. You get the speed of static pages with data that stays current. Adjust the revalidate interval based on how frequently your products change.
Step 3: Build product pages
Product pages are the core of any WooCommerce store. In a headless setup, each product page is a React component that receives data from the WooCommerce API and renders it as static HTML at build time.
Dynamic routes with generateStaticParams
Next.js App Router uses generateStaticParams to pre-build pages for every product in your catalogue. At deploy time, Next.js fetches your product slugs and generates a static HTML page for each one. The result: every product page is served from the CDN edge with zero server-side rendering.
// app/products/[slug]/page.tsx
import { wooFetch } from '@/lib/woocommerce';
export async function generateStaticParams() {
const products = await wooFetch('/products?per_page=100');
return products.map((p: any) => ({ slug: p.slug }));
}
export default async function ProductPage({ params }: { params: { slug: string } }) {
const [product] = await wooFetch(`/products?slug=${params.slug}`);
// Render your product page component
}For stores with more than 100 products, paginate through the API ingenerateStaticParams to capture every product. The WooCommerce REST API returns a maximum of 100 items per request, so loop through pages until you have them all.
Handling product variations
Variable products (sizes, colours, configurations) require additional API calls. Each variation has its own price, stock status, and image. Fetch variations alongside the parent product and build a client-side selector that updates price and availability without a page reload. This is where a headless frontend genuinely outshines traditional WooCommerce — variation switching is instant because the data is already loaded.
<200ms
Headless product page load (CDN edge)
1-3s
Traditional WooCommerce product page load
0ms
Variation switch time (data pre-loaded)
Step 4: Cart and checkout integration
Cart and checkout are the most complex part of a headless WooCommerce migration. Unlike product pages (which are read-only), the cart requires stateful, authenticated interactions with your WordPress backend.
Cart session management
WooCommerce manages cart state server-side using PHP sessions. In a headless setup, you need an alternative approach. The two main options are:
Pros
- WooCommerce Store API (wc/store/v1) — purpose-built for headless carts with session tokens
- Client-side cart state with server-side order creation — cart lives in React state or localStorage
- CoCart plugin — extends the REST API with dedicated cart endpoints and proper session handling
Cons
- Do not try to replicate WooCommerce PHP sessions from JavaScript — it will not work
- Avoid storing sensitive payment data client-side under any circumstances
- Do not build checkout without SSL — enforce HTTPS on every request
The WooCommerce Store API (/wp-json/wc/store/v1/) is the recommended approach. It provides endpoints for adding items, updating quantities, and managing the cart via a nonce-based session token. The API returns the token in response headers, which you store and send with subsequent requests.
Checkout flow
For checkout, you have two strategies. The first is building a fully custom checkout in your Next.js frontend — this gives you complete control over the UX but requires integrating payment gateways directly (Stripe Elements, PayPal SDK, etc.). The second is redirecting to the WordPress checkout page for payment processing — simpler to implement, but loses the performance benefit at the most critical step.
Payment gateway compatibility
Step 5: SEO migration
SEO migration is where most headless projects stumble. Get this wrong and you will lose rankings that took years to build. Get it right and you will likely gain rankings thanks to improved Core Web Vitals. Our headless WooCommerce SEO guide covers this in depth, but here are the critical steps.
URL structure and redirects
Your new frontend must use the same URL structure as the existing site. If your products live at /product/blue-widget/ in WooCommerce, they must live at /product/blue-widget/ in Next.js. If you change URL structures (even removing a trailing slash), set up 301 redirects for every old URL. Next.js handles this in next.config.js:
// next.config.js
module.exports = {
async redirects() {
return [
// Example: old category structure to new
{ source: '/product-category/:slug', destination: '/categories/:slug', permanent: true },
];
},
};Structured data (JSON-LD)
WooCommerce (usually via Yoast or Rank Math) generates Product schema, BreadcrumbList schema, and Organisation schema automatically. In a headless frontend, you must generate this yourself. Embed JSON-LD in your product page's <head> using Next.js metadata:
- Product schema with name, price, availability, SKU, and images
- BreadcrumbList schema matching your site navigation
- Organisation schema on the homepage
- Review/AggregateRating schema if you have product reviews
- Open Graph and Twitter Card meta tags for social sharing
- Canonical URLs to prevent duplicate content issues
XML sitemap
Generate a dynamic sitemap that includes all product pages, category pages, and content pages. Next.js App Router supports this natively with a sitemap.ts file. Fetch your product and category slugs from the WooCommerce API at build time and output valid XML. Submit the new sitemap to Google Search Console immediately after cutover.
Preserving SEO equity
Step 6: Parallel running and testing
Never do a hard cutover. Run both frontends simultaneously while you validate every page, every product, and every checkout scenario.
The parallel running strategy
Deploy your Next.js frontend to a staging URL (e.g. staging.yourstore.com or a Vercel preview URL). Your existing WooCommerce frontend continues serving traffic on the live domain. Both frontends hit the same WordPress backend, so product data, orders, and customer accounts are shared.
Run through this validation checklist before cutting over:
- Every product page renders correctly with images, prices, and variations
- Category and collection pages list the correct products
- Search returns accurate results
- Add to cart works for simple, variable, and grouped products
- Cart updates (quantity changes, removals) function correctly
- Full checkout completes with a test order (use Stripe test mode)
- Order confirmation page displays correctly
- Coupon codes apply and calculate discounts accurately
- All 301 redirects resolve to the correct pages
- Structured data validates in Google Rich Results Test
- XML sitemap is accessible and contains all URLs
- Mobile experience is tested on real devices (not just browser resize)
Test with real payment gateways
Step 7: DNS cutover and go-live
Once testing is complete, the actual cutover is straightforward. You are changing where your domain points — from your WordPress server to your Next.js hosting provider.
The cutover process
Update your domain's DNS records to point to your Next.js hosting provider (Vercel, Netlify, or your server). If you are on Vercel, this means adding a CNAME record. DNS propagation typically takes 15 minutes to a few hours, during which some visitors will see the old site and some will see the new one. Both are functional, so there is no downtime.
Your WordPress backend does not move. It stays on its current server, now serving only API requests. This is a good time to review your WooCommerce hosting strategy — a backend-only WordPress installation has different hosting requirements than a full-stack WordPress site.
Post-cutover checklist
- Verify SSL certificate is active on the new frontend
- Submit updated sitemap to Google Search Console
- Monitor 404 errors in Search Console for missed redirects
- Check Google Analytics and conversion tracking are firing correctly
- Verify WooCommerce webhook notifications still work (order emails, etc.)
- Test the complete purchase flow on the live domain
- Monitor Core Web Vitals in Search Console over the following 28 days
2-6 weeks
Typical migration timeline (build + test + cutover)
90+
Expected Lighthouse performance score post-migration
0
Downtime with parallel running strategy
Common mistakes that derail migrations
Having worked with dozens of WooCommerce headless migrations, these are the mistakes that cause the most damage. Every one of them is avoidable.
Mistake 1: Changing URLs without redirects
/product/blue-widget/ and your new frontend serves it at /products/blue-widget (note: different path, no trailing slash), Google treats these as completely different pages. All ranking equity for the old URL is lost. Map every URL 1:1 or set up 301 redirects.Mistake 2: Forgetting structured data
Mistake 3: Hard cutover without parallel running
Estimating cost and effort
The cost of a headless migration depends on your store's complexity. A simple store with 50 products and standard checkout is fundamentally different from a multi-currency store with 10,000 SKUs, custom pricing rules, and three payment gateways. For a detailed breakdown, read our headless WooCommerce cost guide.
40-200 hrs
Development time (depending on complexity)
$0-20/mo
Vercel hosting for most stores (generous free tier)
5-15%
Conversion rate improvement from faster page loads
The investment pays for itself through improved conversion rates. A store doing $50,000/month in revenue that improves conversion rate by 10% gains $5,000/month. The migration cost is recovered within the first month or two.
Using WPBundle for your migration
Building a headless WooCommerce frontend from scratch means solving cart session management, checkout flows, payment gateway integration, product variation handling, coupon logic, shipping calculations, and tax computation — all before you write a single line of storefront UI code. WPBundle provides a production-ready Next.js storefront with all of this already built, tested, and optimised.
Instead of spending 200+ hours on infrastructure, you focus on design and content. The migration checklist shortens dramatically because the hardest problems — the ones covered in Steps 3 and 4 of this guide — are already solved. For a complete pre-launch walkthrough, see our headless WooCommerce migration checklist.
Related guides
This tutorial covers the technical migration process. For the broader picture — architecture decisions, cost planning, SEO strategy, and API choices — these guides fill in the gaps:
- What is headless WooCommerce? — the architectural overview
- Headless WooCommerce SEO — preserving and improving rankings
- Headless WooCommerce cost breakdown — realistic budgeting
- REST API vs WPGraphQL — choosing your data layer
- WooCommerce hosting strategy — backend hosting for headless
- Migration checklist — the operational pre-launch list
- Elementor to headless — page builder-specific migration
Ready to go headless?
Join the WPBundle waitlist and get beta access completely free.
Join the Waitlist