Use WooCommerce as a headless e-commerce backend for Bolt-generated React frontends. Generate REST API keys in WordPress (WooCommerce → Settings → Advanced → REST API), then build a Next.js API route in Bolt that proxies WooCommerce REST calls — this bypasses CORS and keeps your secret key server-side. Fetch products, manage cart state in React, and redirect to WooCommerce's checkout for payment processing.
Building a Headless WooCommerce Frontend with Bolt.new
WooCommerce powers over 6 million online stores worldwide, but its default theme-based frontend can feel limiting compared to a modern React single-page application. The headless approach separates the frontend (your Bolt-generated React app) from the backend (WooCommerce on WordPress) while keeping WooCommerce's mature product management, payment processing, and order management capabilities. You get full control over the user experience while WooCommerce handles all the commerce logic.
The WooCommerce REST API (v3) provides endpoints for everything a storefront needs: products, product variations, categories, tags, customers, orders, and coupons. Authentication uses API key pairs (consumer_key and consumer_secret) generated in WordPress, passed as query parameters or HTTP Basic Auth. The key architectural decision for a Bolt integration is to always proxy these calls through Next.js API routes rather than calling WooCommerce directly from the browser. Direct browser-to-WooCommerce calls face two problems: CORS (WordPress typically does not allow cross-origin requests from arbitrary domains) and security (the consumer_secret would be exposed in browser network requests).
For the shopping flow, there is one important constraint: WooCommerce's payment processing happens on the WordPress checkout page, not in your Bolt frontend. The standard headless WooCommerce pattern is to build a custom browsing, search, and cart experience in React, then redirect customers to the standard WooCommerce checkout URL (/checkout) for payment. This keeps PCI compliance on WooCommerce's side and avoids the complexity of building your own payment UI. For fully custom checkout experiences, WooCommerce's Blocks API and the WooPayments plugin support headless checkout — but that is a more advanced integration beyond this tutorial.
Integration method
WooCommerce runs on a self-hosted or managed WordPress installation and exposes a REST API at /wp-json/wc/v3/. Bolt generates the React frontend and Next.js API routes that proxy all WooCommerce API calls — keeping your secret key server-side and handling CORS between the browser and your WordPress domain. Product browsing and cart management work in the Bolt WebContainer preview. WooCommerce webhook events (order.created, order.updated) require a deployed URL to receive since the WebContainer has no public address.
Prerequisites
- A live WordPress installation with WooCommerce plugin active (self-hosted or managed WordPress — WooCommerce.com, Pressable, WP Engine, etc.)
- WordPress admin access to generate WooCommerce REST API keys
- Your WordPress site's domain (e.g., https://yourstore.com) and WooCommerce REST API accessible at /wp-json/wc/v3/
- A Bolt.new project using Next.js for server-side API routes
- Products and at least one category set up in WooCommerce so you can verify the integration is working
Step-by-step guide
Generate WooCommerce REST API Keys
Generate WooCommerce REST API Keys
WooCommerce REST API authentication uses consumer_key and consumer_secret pairs generated per user in the WordPress admin. Each key pair is tied to a WordPress user account and inherits that user's permissions. For a full-featured integration (reading and writing products, orders, customers), you need keys associated with a user who has the Shop Manager or Administrator role. To generate API keys: log into WordPress admin → WooCommerce → Settings → Advanced tab → REST API. Click 'Add Key'. Enter a description (e.g., 'Bolt Development'), select the WordPress user (use Administrator for full access), and set Permissions to 'Read/Write'. Click 'Generate API Key'. You will see the Consumer Key and Consumer Secret displayed ONCE — copy both immediately and store them securely. The consumer_secret is never shown again. WooCommerce uses HTTP Basic Auth for the REST API: the consumer_key is the username and consumer_secret is the password in the Authorization header. For HTTPS endpoints (required for production), Basic Auth is secure. For development with an HTTP-only WordPress (common in local development), WooCommerce supports passing credentials as query parameters (?consumer_key=xxx&consumer_secret=yyy) since Basic Auth over HTTP would expose credentials. For your Bolt project, store the keys in .env and always use Basic Auth via the server-side Next.js API route — never expose credentials to the browser. If you are using a managed WordPress host (WP Engine, Pressable, Cloudways), ensure your host has not blocked the /wp-json/ endpoint or added authentication requirements. Some hosts require you to whitelist API access from specific IPs, which is problematic with Netlify/Vercel's dynamic serverless IP ranges — in that case, configure the WordPress host to allow access from all IPs with valid API credentials.
Create a .env file with: WOOCOMMERCE_URL=https://yourstore.com (no trailing slash), WOOCOMMERCE_KEY=ck_your_consumer_key, WOOCOMMERCE_SECRET=cs_your_consumer_secret as placeholders. Create lib/woocommerce.ts with a wooRequest helper that makes authenticated requests to the WooCommerce REST API using HTTP Basic Auth. Export typed helper functions: getProducts(params), getProduct(id), getCategories(), getOrders(params), getOrder(id), updateOrderStatus(id, status). Add TypeScript interfaces for WooCommerceProduct, WooCommerceOrder, and WooCommerceCategory.
Paste this in Bolt.new chat
1// lib/woocommerce.ts2const BASE_URL = process.env.WOOCOMMERCE_URL!;3const KEY = process.env.WOOCOMMERCE_KEY!;4const SECRET = process.env.WOOCOMMERCE_SECRET!;56const AUTH = Buffer.from(`${KEY}:${SECRET}`).toString('base64');78export interface WooProduct {9 id: number;10 name: string;11 slug: string;12 permalink: string;13 price: string;14 regular_price: string;15 sale_price: string;16 stock_status: 'instock' | 'outofstock' | 'onbackorder';17 stock_quantity: number | null;18 images: Array<{ src: string; alt: string }>;19 categories: Array<{ id: number; name: string; slug: string }>;20 short_description: string;21}2223export interface WooOrder {24 id: number;25 number: string;26 status: string;27 total: string;28 billing: { first_name: string; last_name: string; email: string };29 line_items: Array<{ name: string; quantity: number; total: string }>;30 date_created: string;31}3233async function wooRequest<T>(34 endpoint: string,35 options: RequestInit = {}36): Promise<T> {37 const url = `${BASE_URL}/wp-json/wc/v3/${endpoint}`;38 const res = await fetch(url, {39 ...options,40 headers: {41 Authorization: `Basic ${AUTH}`,42 'Content-Type': 'application/json',43 ...options.headers,44 },45 });46 if (!res.ok) {47 const err = await res.text();48 throw new Error(`WooCommerce API error ${res.status}: ${err}`);49 }50 return res.json();51}5253export const woo = {54 getProducts: (params: Record<string, string | number> = {}) => {55 const qs = new URLSearchParams(params as Record<string, string>).toString();56 return wooRequest<WooProduct[]>(`products${qs ? '?' + qs : ''}`);57 },58 getProduct: (id: number) => wooRequest<WooProduct>(`products/${id}`),59 getCategories: () => wooRequest<Array<{ id: number; name: string; slug: string; count: number }>>('products/categories?per_page=100'),60 getOrders: (params: Record<string, string> = {}) => {61 const qs = new URLSearchParams(params).toString();62 return wooRequest<WooOrder[]>(`orders${qs ? '?' + qs : ''}`);63 },64 updateOrderStatus: (id: number, status: string) =>65 wooRequest(`orders/${id}`, { method: 'PUT', body: JSON.stringify({ status }) }),66};Pro tip: WooCommerce's X-WP-Total and X-WP-TotalPages response headers provide total product count and pagination info. Parse them from the response headers to implement proper pagination in your frontend — the API returns up to 100 products per page.
Expected result: The WooCommerce API client is configured. The woo.getProducts() function returns your store's products when called from a test API route. The Bolt preview can display live WooCommerce product data via the proxy API routes.
Build Product and Category API Routes
Build Product and Category API Routes
Create Next.js API routes that serve as the proxy layer between your React frontend and WooCommerce. The frontend never calls WooCommerce directly — it only calls your own routes at /api/products, /api/categories, etc. This proxy pattern solves CORS (the server-to-server call does not have browser CORS restrictions), keeps credentials server-side, and lets you transform or filter the WooCommerce response before sending it to the browser. The product listing route should accept query parameters for filtering: category (WooCommerce category slug or ID), page (for pagination), per_page (defaults to 12 for a typical product grid), orderby (date, price, popularity, rating), order (asc/desc), min_price, max_price, and search (for text search). Each of these maps directly to WooCommerce REST API query parameters — your route validates and passes them through. For performance, consider the response size: WooCommerce's product objects include many fields your frontend may not need. Project only the fields you use (id, name, slug, permalink, price, regular_price, sale_price, stock_status, images, categories, short_description) to reduce response payload size. You can do this by spreading the relevant fields from the WooCommerce response into a leaner object. The categories route fetches all categories in a single call (WooCommerce has a max of 100 per page, sufficient for most stores) and sorts them by name. Return them as a simple array of {id, name, slug, count} objects for the frontend sidebar filter. In Bolt's WebContainer, both routes make outbound HTTPS calls to your WordPress server — these work perfectly during development without any CORS issues.
Create Next.js API routes for WooCommerce. Build: GET /api/products (accepts category, page, per_page, orderby, order, min_price, max_price, search query params — proxy to WooCommerce, return lean product objects with only needed fields), GET /api/products/[id] (single product detail), GET /api/categories (all categories sorted by name), GET /api/products/search (accepts q param, proxy to WooCommerce search). Each route imports from lib/woocommerce.ts and handles errors with appropriate HTTP status codes. Include pagination headers (X-Total-Count) in the products response.
Paste this in Bolt.new chat
1// app/api/products/route.ts2import { NextResponse } from 'next/server';3import { woo } from '@/lib/woocommerce';45export async function GET(request: Request) {6 try {7 const { searchParams } = new URL(request.url);8 const params: Record<string, string> = {};9 const allowed = ['category', 'page', 'per_page', 'orderby', 'order', 'min_price', 'max_price', 'search'];10 for (const key of allowed) {11 const val = searchParams.get(key);12 if (val) params[key] = val;13 }14 if (!params.per_page) params.per_page = '12';1516 const products = await woo.getProducts(params);17 const lean = products.map(p => ({18 id: p.id,19 name: p.name,20 slug: p.slug,21 permalink: p.permalink,22 price: p.price,23 regular_price: p.regular_price,24 sale_price: p.sale_price,25 stock_status: p.stock_status,26 image: p.images[0] ?? null,27 categories: p.categories,28 }));29 return NextResponse.json(lean);30 } catch (error: unknown) {31 const e = error as { message: string };32 return NextResponse.json({ error: e.message }, { status: 500 });33 }34}Pro tip: WooCommerce product search is basic — it searches product names and descriptions. For a better search experience on larger catalogs, consider adding a search plugin to WordPress (like Relevanssi) or using a dedicated search service like Algolia synced from WooCommerce.
Expected result: GET /api/products returns a lean array of your store's products. GET /api/categories returns all categories. Filtering by category and searching work correctly. The Bolt preview displays live WooCommerce data through the proxy routes.
Build the React Storefront UI
Build the React Storefront UI
With the API routes returning product data, prompt Bolt to generate the complete storefront UI. The shopping experience consists of several interconnected components: a product catalog page with filtering sidebar, individual product cards, a product detail page, a cart that manages state client-side, and a checkout handoff that redirects to WooCommerce. For cart state management, the standard approach is React Context or Zustand — store cart items (product ID, name, price, quantity, image) in local state, calculate totals client-side, and let WooCommerce handle actual order creation. When a customer clicks 'Checkout', construct a WooCommerce cart URL that pre-fills their selected items using the ?add-to-cart= query parameter or the WooCommerce REST API's order endpoint. The simplest flow is to link to the WooCommerce product pages or use the ?add-to-cart={product_id}&quantity={qty} URL parameter to add items to the WooCommerce server-side cart, then redirect to /checkout. Product images served from your WordPress domain will work in the Bolt preview since they are loaded via standard HTML img tags (the browser fetches them directly). Cart state should persist across page refreshes using localStorage — store the cart array in localStorage and rehydrate it on app load. For product detail pages, use Next.js dynamic routes ([slug]/page.tsx) that fetch product data server-side via the /api/products/[id] route. Include the product's full description, all images in a gallery, variant selection for variable products (fetch variations via /api/products/[id]/variations), quantity selector, and the Add to Cart button that updates local cart state.
Build the complete WooCommerce storefront UI. Create: (1) A CategorySidebar component that fetches /api/categories and renders filter checkboxes. (2) A ProductCard component with image, name, price (show sale price with strikethrough if on sale), stock badge, and Add to Cart button. (3) A ProductGrid component fetching /api/products with active filters. (4) A CartDrawer that slides in from the right showing cart items, quantities, subtotal, and a 'Checkout' button. (5) Cart state managed with Zustand, persisted to localStorage. The Checkout button should link to NEXT_PUBLIC_WOOCOMMERCE_URL/checkout (not /api/). Style with Tailwind CSS.
Paste this in Bolt.new chat
1// Store cart items and build WooCommerce cart redirect URL2// When user clicks Checkout, redirect to WooCommerce:3// https://yourstore.com/checkout4// To pre-fill cart items, use WooCommerce's URL approach:5// https://yourstore.com/?add-to-cart=123&quantity=26// Or for multiple items, use the REST API to create an order draft7// and redirect to the order payment URL.89// Example: Add to cart via WooCommerce URL parameter10export function buildAddToCartUrl(baseUrl: string, productId: number, qty: number): string {11 return `${baseUrl}/?add-to-cart=${productId}&quantity=${qty}`;12}1314// Example: Redirect to WooCommerce checkout with items in query15export function buildCheckoutUrl(baseUrl: string): string {16 return `${baseUrl}/checkout`;17}Pro tip: WooCommerce's session-based cart (server-side) and your Bolt app's local cart state are separate. The cleanest checkout flow is to show an 'Order Summary' review screen in your React app, then redirect to WooCommerce /checkout where the actual payment is processed. Pre-populating WooCommerce's server cart before redirect can be done via the ?add-to-cart URL parameter for simple products.
Expected result: The storefront UI renders product listings from WooCommerce, with working category filters and search. The cart drawer tracks added items with quantities and subtotal. Clicking Checkout redirects to the WooCommerce checkout page.
Configure CORS on WordPress for Bolt Development
Configure CORS on WordPress for Bolt Development
In some WordPress configurations, direct API calls from a different domain return CORS errors. Since your Bolt app uses Next.js API routes as a proxy (server-to-server calls), CORS does not affect the product data routes — server-side calls do not have CORS restrictions. However, if you need any client-side direct calls to WordPress (e.g., for WordPress REST API features or the Gutenberg block editor), CORS configuration on WordPress becomes relevant. To configure CORS headers on WordPress, add the following to your WordPress theme's functions.php or a custom plugin. This allows the Bolt preview domain and your deployed domain to make cross-origin requests to the WordPress REST API. For the WooCommerce integration pattern in this tutorial (Next.js proxy routes handling all API calls server-side), you do not need to modify WordPress CORS settings — all WooCommerce API calls go through your Next.js routes at /api/*, which are same-origin from the browser's perspective. The browser calls https://your-bolt-app.netlify.app/api/products and the Next.js route calls https://yourstore.com/wp-json/wc/v3/products server-to-server. The only situation where CORS matters directly is if you need to fetch from WordPress's media library or other endpoints client-side. For the typical headless WooCommerce storefront, the proxy pattern is sufficient and preferred.
Add a CORS configuration note to my WooCommerce integration. Create a PHP snippet file (cors-setup.php) that can be added to WordPress functions.php to allow cross-origin requests from specific domains. Also create a Next.js middleware.ts that adds appropriate CORS headers to my /api/ routes for any future client-side usage. Set NEXT_PUBLIC_WOOCOMMERCE_URL in .env.example as the public WooCommerce store URL (safe to expose — used for checkout redirect links).
Paste this in Bolt.new chat
1<?php2// Add to your WordPress theme's functions.php3// or create a simple plugin4// This allows your Bolt/Netlify app to call the WP REST API directly if needed56add_action('init', function() {7 $allowed_origins = [8 'https://your-bolt-app.netlify.app',9 'https://your-bolt-preview-url.bolt.new',10 'http://localhost:3000', // local Next.js dev11 ];1213 $origin = $_SERVER['HTTP_ORIGIN'] ?? '';14 if (in_array($origin, $allowed_origins)) {15 header('Access-Control-Allow-Origin: ' . $origin);16 header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');17 header('Access-Control-Allow-Headers: Authorization, Content-Type, X-WP-Nonce');18 header('Access-Control-Allow-Credentials: true');19 }2021 if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {22 status_header(200);23 exit();24 }25});Pro tip: If your WordPress host is on a different domain from your Bolt app, test whether you actually get CORS errors before modifying WordPress. With the proxy pattern (all API calls via Next.js routes), you likely will not see any CORS issues during development.
Expected result: The CORS configuration is documented and the PHP snippet is ready to add to WordPress if needed. The Next.js API route proxy continues to work for all product data without any CORS modifications to WordPress.
Deploy and Set Up WooCommerce Webhooks
Deploy and Set Up WooCommerce Webhooks
Deploy your Bolt project to Netlify to get a stable public URL, then configure WooCommerce webhooks for real-time order notifications. During Bolt WebContainer development, your app can call WooCommerce's API outbound (product fetching, order creation) but WooCommerce cannot send incoming webhook events to the WebContainer — there is no public URL for WooCommerce to post to. Deploy to Netlify: connect via Bolt Settings → Applications → Netlify → Publish. In Netlify Dashboard → Site Settings → Environment Variables, add WOOCOMMERCE_URL, WOOCOMMERCE_KEY, WOOCOMMERCE_SECRET, and NEXT_PUBLIC_WOOCOMMERCE_URL (the public store URL for checkout links). Trigger a redeploy after adding variables. For WooCommerce webhooks: in WordPress admin, go to WooCommerce → Settings → Advanced → Webhooks. Click 'Add webhook'. Set the Delivery URL to https://your-netlify-app.netlify.app/api/webhooks/woocommerce. Choose a Topic (e.g., 'Order created', 'Order updated'). Set Status to Active. WooCommerce will generate a Secret (used to verify incoming payloads) — copy this and add it as WOOCOMMERCE_WEBHOOK_SECRET to your Netlify environment variables. Create the webhook handler at app/api/webhooks/woocommerce/route.ts. WooCommerce sends a POST request with the order data as JSON and includes an X-WC-Webhook-Signature header (HMAC-SHA256 of the payload using the webhook secret). Always verify this signature before processing the webhook to prevent unauthorized requests. After verification, process the event — update your database, send notification emails, trigger fulfillment logic, etc.
Create a WooCommerce webhook handler at app/api/webhooks/woocommerce/route.ts. The route should: (1) Read the raw request body as text, (2) Verify the X-WC-Webhook-Signature header using HMAC-SHA256 with WOOCOMMERCE_WEBHOOK_SECRET from env, (3) Parse the JSON body and log the webhook topic (from X-WC-Webhook-Topic header) and order ID, (4) Handle order.created and order.updated topics by logging order details, (5) Return 200 immediately on success. Create netlify.toml for deployment with @netlify/plugin-nextjs.
Paste this in Bolt.new chat
1// app/api/webhooks/woocommerce/route.ts2import { createHmac } from 'crypto';3import { NextResponse } from 'next/server';45export async function POST(request: Request) {6 const body = await request.text();7 const signature = request.headers.get('x-wc-webhook-signature');8 const topic = request.headers.get('x-wc-webhook-topic');910 // Verify HMAC-SHA256 signature11 const expected = createHmac('sha256', process.env.WOOCOMMERCE_WEBHOOK_SECRET!)12 .update(body)13 .digest('base64');1415 if (signature !== expected) {16 console.error('WooCommerce webhook signature mismatch');17 return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });18 }1920 let data: Record<string, unknown>;21 try {22 data = JSON.parse(body);23 } catch {24 return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 });25 }2627 console.log(`WooCommerce webhook received: ${topic}`, {28 orderId: data.id,29 status: data.status,30 });3132 switch (topic) {33 case 'order.created':34 // Handle new order: send confirmation email, update inventory, etc.35 break;36 case 'order.updated':37 // Handle order status change: notify customer, trigger fulfillment38 break;39 }4041 return NextResponse.json({ received: true });42}Pro tip: WooCommerce webhooks have a built-in retry mechanism — if your endpoint returns a non-2xx status or times out (30 second limit), WooCommerce retries the webhook up to 5 times with exponential backoff. Always respond with 200 quickly and process the webhook payload asynchronously if needed.
Expected result: The app is deployed to Netlify. WooCommerce webhooks are registered with the Netlify URL and fire when orders are created or updated. The webhook handler verifies signatures and processes order events.
Common use cases
Custom Product Catalog with React UI
Replace WooCommerce's default theme frontend with a modern React UI that fetches product data from the WooCommerce REST API. The Bolt app renders a fast, interactive product catalog with filtering by category, price range, and attributes — all calling your own Next.js API routes that proxy to WooCommerce. Customers who add items to cart are redirected to the standard WooCommerce checkout for payment.
Build a headless WooCommerce storefront. Create a Next.js API route at app/api/products/route.ts that fetches products from my WooCommerce REST API (WOOCOMMERCE_URL, WOOCOMMERCE_KEY, WOOCOMMERCE_SECRET from env). Support query params: category, page, per_page, orderby, min_price, max_price. The route should use HTTP Basic Auth with the consumer_key:consumer_secret. Build a product listing page with a sidebar filter (categories fetched from /api/categories), product grid with images, prices, and 'Add to Cart' buttons, and a cart drawer with a 'Checkout' link to the WooCommerce checkout URL.
Copy this prompt to try it in Bolt.new
WooCommerce Order Management Dashboard
Build an internal order management dashboard for store admins that surfaces WooCommerce orders with filtering by status, date, and customer. Admins can update order status, view line items and shipping details, and trigger refunds — all via API routes calling the WooCommerce REST API with admin-level credentials. This is an internal tool, not customer-facing.
Build a WooCommerce order management dashboard. Create API routes: GET /api/orders (list orders with status, date_after, date_before filters), GET /api/orders/[id] (order detail with line items), POST /api/orders/[id]/status (update order status to 'processing', 'completed', 'cancelled', 'refunded'). Build a dashboard UI with an orders table showing order number, customer name, total, status badge, and date. Clicking a row opens a detail modal with full line items. Add a status dropdown to update order status. Use WOOCOMMERCE_URL, WOOCOMMERCE_KEY, WOOCOMMERCE_SECRET env vars.
Copy this prompt to try it in Bolt.new
Product Search with Live Inventory
Add a high-performance product search UI to a WooCommerce store that shows real-time stock availability. Customers type in a search bar and see filtered results with stock status indicators. The search calls a debounced API route that queries WooCommerce's search endpoint and enriches results with stock quantity and backorder status.
Build a product search component for my WooCommerce store. Create a GET /api/products/search route that accepts a 'q' query param and calls WooCommerce's products endpoint with the search parameter. Return product name, price, image, stock_status, stock_quantity, slug, and permalink fields. Build a React SearchBar component with 300ms debounce that shows a dropdown of up to 8 results as the user types, with a product thumbnail, name, price, and a 'In Stock' / 'Out of Stock' badge. Clicking a result redirects to the WooCommerce product page URL.
Copy this prompt to try it in Bolt.new
Troubleshooting
WooCommerce API returns 401 Unauthorized: 'woocommerce_rest_cannot_view'
Cause: The API key permission is set to 'Read' but the request requires 'Write' access, or the consumer_key and consumer_secret are incorrect. Also occurs when the REST API is disabled in WordPress.
Solution: In WordPress admin → WooCommerce → Settings → Advanced → REST API, verify the key exists and its permission is set to 'Read/Write'. Regenerate the key if needed (you will need to update .env with the new values). Also confirm the WooCommerce REST API is enabled in Settings → Permalinks — resave permalink settings.
fetch failed with 'getaddrinfo ENOTFOUND yourstore.com' or connection refused in Bolt preview
Cause: The WOOCOMMERCE_URL in .env points to a domain that is not publicly accessible (localhost, a local development URL, or a domain that does not have WooCommerce installed).
Solution: WOOCOMMERCE_URL must be the public HTTPS URL of your live WordPress site. WooCommerce must be accessible from the internet since Bolt's Next.js server makes outbound calls to it. For local WordPress development, use a tunneling service like ngrok to expose your local WordPress publicly, then use the ngrok URL in .env.
Product images return 403 Forbidden or do not display in the Bolt preview
Cause: WordPress image URLs may require authentication to access, or your WordPress host has hotlink protection enabled that blocks requests from the Bolt preview domain.
Solution: Check if the image URLs work when pasted directly into a new browser tab without any authentication. If they require auth, set up an image proxy API route. If the host has hotlink protection, add the Bolt preview domain to the allowlist in the host's control panel or .htaccess configuration.
WooCommerce webhook signature verification fails after deployment to Netlify
Cause: The WOOCOMMERCE_WEBHOOK_SECRET was not added to Netlify's environment variables, or the request body is being parsed (destroying the raw body) before signature verification.
Solution: Ensure you read the request body as raw text (await request.text()) BEFORE parsing it as JSON. The HMAC must be computed on the exact raw bytes WooCommerce sent, not a re-serialized JSON string. Add WOOCOMMERCE_WEBHOOK_SECRET to Netlify environment variables and redeploy.
1// CORRECT: read raw body first, then parse2const body = await request.text(); // raw string for HMAC3const signature = computeHmac(body, process.env.WOOCOMMERCE_WEBHOOK_SECRET!);4const data = JSON.parse(body); // parse after verification56// WRONG: parsing body first loses the raw bytes7// const data = await request.json(); // body stream already consumedBest practices
- Always proxy WooCommerce API calls through Next.js server-side routes — never call the WooCommerce REST API directly from the browser to keep credentials secure and avoid CORS issues
- Store WooCommerce consumer_key and consumer_secret in .env as server-only variables without NEXT_PUBLIC_ prefix — they grant full access to your store's data
- For the checkout flow, redirect to WooCommerce's native checkout page rather than building a custom payment UI — this keeps PCI compliance on WooCommerce's side
- During Bolt WebContainer development, outbound calls to WooCommerce work fine — only incoming webhooks require a deployed URL
- Verify WooCommerce webhook signatures using HMAC-SHA256 before processing any webhook payload to prevent unauthorized access to your order handler
- Read the X-WP-Total response header to implement proper pagination — WooCommerce does not include total count in the response body
- Use the WooCommerce REST API v3 (/wp-json/wc/v3/) rather than older versions — it supports all current features including variable products, coupons, and shipping zones
- Test your WooCommerce API keys by fetching a simple endpoint like /wp-json/wc/v3/system_status before building the full integration
Alternatives
Ecwid is a hosted SaaS e-commerce solution that embeds into any website without requiring WordPress — simpler setup and maintenance than a self-hosted WooCommerce installation.
BigCommerce is a fully hosted e-commerce platform with a mature headless API and better native performance than WooCommerce on WordPress, with no server maintenance required.
For simple payment collection without full e-commerce inventory management, Stripe Checkout and Payment Links are faster to integrate than a full WooCommerce headless setup.
WooCommerce extends WordPress, so you can use the WordPress REST API alongside WooCommerce to access posts, pages, and custom post types for content alongside your product catalog.
Frequently asked questions
Does WooCommerce work with Bolt.new during development in the WebContainer?
Yes, for outbound API calls. Bolt's WebContainer can make HTTPS requests to your WordPress/WooCommerce server, so product fetching, order queries, and updates all work in the Bolt preview. What requires a deployed URL is receiving WooCommerce webhooks — WooCommerce cannot POST to the browser-based WebContainer. For webhook testing, deploy to Netlify first.
Can I build a fully custom checkout experience without redirecting to WooCommerce?
Yes, but it requires additional setup. WooCommerce's Blocks-based checkout supports headless patterns via the Store API (/wp-json/wc/store/v1/). You can create a cart, add items, and submit the order entirely via API calls, with payment handled by a WooCommerce payment gateway that supports headless. This is more complex than the redirect approach and requires WooCommerce 6.9+ with blocks support. For most projects, redirecting to the native WooCommerce checkout is faster to implement and maintains full payment gateway compatibility.
How do I handle product variations (size, color, etc.) in a headless WooCommerce integration?
WooCommerce variable products have a parent product with attributes and child variation objects. Fetch the parent product to get the attribute options, then fetch /api/products/[id]/variations to get all available variations with their specific prices, stock levels, and images. When a customer selects a combination of attributes, find the matching variation object and use its ID for the add-to-cart action.
What happens to the WooCommerce cart if a user browses my Bolt app and then goes to the WordPress checkout?
WooCommerce maintains two cart systems: the server-side session cart (stored in WordPress via cookies/sessions) and whatever local state you manage in your React app. The ?add-to-cart URL parameter is the simplest bridge: before redirecting to /checkout, redirect through /?add-to-cart=PRODUCT_ID&quantity=N for each item. This adds items to WooCommerce's server session, and /checkout then shows those items. More advanced approaches use the Store API to programmatically build the WooCommerce cart.
Can I use WooCommerce with Bolt.new if my WordPress site is behind a CDN or security plugin?
Security plugins like Wordfence or hosting-level WAFs sometimes block API requests from unfamiliar IPs. Since Netlify and Vercel use dynamic IP addresses for serverless functions, IP-based allowlisting will not work reliably. Instead, ensure your WooCommerce API keys are set to 'Read/Write' permission, and configure your security plugin to allow requests with valid WooCommerce authentication headers rather than filtering by IP. Check your security plugin's logs if API calls fail after deployment.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation