How to Integrate WooCommerce with Your Headless CMS
Connect WooCommerce to a headless CMS so product pages, buying guides, and campaign content stay in sync with your catalog without copying fields by hand.
What is WooCommerce?
WooCommerce is an open-source commerce plugin for WordPress that handles products, carts, checkout, taxes, shipping, coupons, orders, and payments. It's used by small stores, subscription businesses, B2B sellers, and custom commerce teams that want control over their storefront and WordPress-based operations. Its REST API lets external systems create and update products, variations, categories, coupons, and orders.
Why integrate WooCommerce with a headless CMS?
WooCommerce is good at commerce operations: prices, SKUs, stock status, checkout, taxes, and orders. A headless CMS is better for editorial product content: comparison tables, fit guides, care instructions, localized landing pages, campaign copy, and reusable product storytelling. When those systems aren't connected, teams end up copying the same product title, description, image, and category fields between tools. That creates drift fast, especially when you're updating 50 SKUs for a sale or launching a product in 3 regions.
Architecture overview
A common WooCommerce and Sanity integration starts when an editor publishes or updates product content in Sanity Studio. The product document is structured in the Content Lake with fields like sku, title, slug, shortDescription, productImages, category references, priceCopy, and wooProductId. A GROQ query selects only the fields WooCommerce needs, including joined data from referenced category and image documents. On publish, a Sanity webhook fires with a filtered payload, for example only documents where _type == "product". That event can call a Sanity Function or your own webhook endpoint. The server-side code validates the webhook secret, fetches the latest product document from the Content Lake with @sanity/client, maps Sanity fields to WooCommerce fields, and calls the WooCommerce REST API. For a new product, it sends POST /wp-json/wc/v3/products. For an existing product with wooProductId, it sends PUT /wp-json/wc/v3/products/{id}. For deleted or unpublished content, you can set status to "draft" instead of deleting the WooCommerce product. The end user then sees the result through your storefront. That might be a WordPress theme reading WooCommerce data, a custom Next.js storefront calling WooCommerce for cart and checkout, or a product detail page that combines Sanity editorial content with WooCommerce price and availability. The trade-off is that you need a clear source-of-truth rule. For example, Sanity owns product storytelling and merchandising copy, while WooCommerce owns price, stock, tax, checkout, and orders.
Common use cases
Editorial product detail pages
Use Sanity for rich product copy, buying guides, and image storytelling, then sync core fields to WooCommerce products by SKU.
Campaign and sale launches
Publish seasonal product collections in Sanity and update WooCommerce categories, tags, and product visibility when the campaign goes live.
Localized commerce content
Structure regional product names, descriptions, and care instructions in Sanity, then send the right fields to the matching WooCommerce store.
Shopping agents with product context
Use Agent Context to let production AI agents answer product questions from structured Sanity content while WooCommerce handles cart and checkout.
Step-by-step integration
- 1
Set up WooCommerce API access
Install WooCommerce in WordPress, finish the store setup flow, then go to WooCommerce > Settings > Advanced > REST API. Create a key with Read/Write access, copy the consumer key and consumer secret, and store them as environment variables. For Node.js projects, install the official REST API client with npm install @woocommerce/woocommerce-rest-api.
- 2
Model product content in Sanity Studio
Create a product schema with fields that map cleanly to WooCommerce: sku, title, slug, shortDescription, body, images, category references, tags, status, and wooProductId. Keep commerce-owned fields, like live inventory and order data, in WooCommerce unless you have a specific reason to mirror them.
- 3
Create the sync trigger
Add a Sanity webhook for create, update, and delete events on product documents, using a GROQ filter such as _type == "product". Point it at a Sanity Function or webhook listener, and include a secret so your endpoint can reject unsigned requests.
- 4
Fetch and map the content
In your handler, use @sanity/client and GROQ to fetch the full product document by _id. Map Sanity fields to WooCommerce fields, such as name, slug, sku, short_description, description, categories, images, status, and tags.
- 5
Call the WooCommerce REST API
If the Sanity document already has wooProductId, update that product with the WooCommerce API. If it doesn't, create a product, then write the returned WooCommerce id back to Sanity so future publishes update the same product.
- 6
Test the storefront flow
Publish a test product, confirm it appears in WooCommerce, check product images and categories, then run a test checkout. If you're building a custom frontend, test both sides: Sanity for editorial content and WooCommerce for price, cart, and checkout.
Code example
import type { NextApiRequest, NextApiResponse } from 'next'
import { createClient } from '@sanity/client'
import WooCommerceRestApi from '@woocommerce/woocommerce-rest-api'
const sanity = createClient({
projectId: process.env.SANITY_PROJECT_ID!,
dataset: process.env.SANITY_DATASET!,
apiVersion: '2025-01-01',
token: process.env.SANITY_WRITE_TOKEN!,
useCdn: false
})
const woo = new WooCommerceRestApi({
url: process.env.WOO_URL!,
consumerKey: process.env.WOO_KEY!,
consumerSecret: process.env.WOO_SECRET!,
version: 'wc/v3'
})
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.headers['x-sanity-secret'] !== process.env.SANITY_WEBHOOK_SECRET) {
return res.status(401).json({ ok: false })
}
const id = req.body._id
const product = await sanity.fetch(`*[_id == $id][0]{
_id, title, sku, slug, shortDescription, body, wooProductId,
"imageUrls": images[].asset->url,
categories[]->{title, wooCategoryId}
}`, { id })
const payload = {
name: product.title,
slug: product.slug.current,
sku: product.sku,
short_description: product.shortDescription,
description: product.body?.[0]?.children?.[0]?.text || '',
images: (product.imageUrls || []).map((src: string) => ({ src })),
categories: (product.categories || [])
.filter((c: any) => c.wooCategoryId)
.map((c: any) => ({ id: c.wooCategoryId })),
status: 'publish'
}
const response = product.wooProductId
? await woo.put(`products/${product.wooProductId}`, payload)
: await woo.post('products', payload)
if (!product.wooProductId) {
await sanity.patch(product._id).set({ wooProductId: response.data.id }).commit()
}
return res.status(200).json({ ok: true, wooProductId: response.data.id })
}How Sanity + WooCommerce works
Build your WooCommerce integration on Sanity
Sanity gives you the structured content foundation, real-time event system, and flexible APIs to connect product content with WooCommerce.
Start building free →CMS approaches to WooCommerce
| Capability | Traditional CMS | Sanity |
|---|---|---|
| Product content modeling | Product copy often lives inside page fields or WordPress editor content, which can make reuse across product pages, guides, and apps harder. | Schema-as-code in Sanity Studio lets you model SKUs, product families, fit data, care instructions, bundles, and category references as typed content. |
| WooCommerce sync timing | Teams often update WooCommerce manually or run scheduled jobs that can leave product content out of date for minutes or hours. | Webhooks and Functions can trigger on publish events, fetch the latest document, and call the WooCommerce REST API without polling. |
| Field-level API control | Integrations may receive full pages or rendered HTML, so the sync layer has to extract the fields WooCommerce needs. | GROQ can fetch exact fields and resolve references in one query, such as product data plus category ids, image URLs, and merchandising copy. |
| Multi-channel commerce delivery | The website is usually the primary target, and extra channels often need duplicated content or custom exports. | The same product content can feed web, mobile, WooCommerce, and production AI agents through Agent Context. |
| Editorial workflow for commerce teams | Merchandising changes can be tied to page editing, which makes staged launches and review steps harder to coordinate. | Sanity Studio can be customized for merchandisers, with fields, previews, validation, Comments, Tasks, Content Releases, and scheduled publishing. |
Keep building
Explore related integrations to complete your content stack.
Sanity + Shopify
Structure product storytelling in Sanity while Shopify handles catalog operations, checkout, and orders.
Sanity + Stripe
Connect structured pricing pages, plans, and subscription content in Sanity with Stripe products and checkout flows.
Sanity + BigCommerce
Use Sanity for flexible commerce content while BigCommerce handles product catalog, cart, checkout, and order operations.