A headless WooCommerce storefront built with Next.js 16, React 19, TypeScript, and the CoCart REST API.
- Full WooCommerce Integration - Products, categories, variations, cart, and checkout
- CoCart-Powered Cart & Auth - Session persistence and cart operations via the CoCart SDK
- Type-safe API Layer - Comprehensive TypeScript definitions for WooCommerce
- Client-side Cart - Persistent shopping cart with localStorage
- Native Headless Checkout - Order creation and payment via CoCart Plus, no redirect to WooCommerce
- Customer Accounts - Login, registration, and an account dashboard (orders, downloads, profile) native to the app
- Server-side Pagination - Efficient product browsing with filters
- Blog Support - WordPress posts, categories, tags, and authors
- Cache Revalidation - Automatic updates when content changes
- Dark Mode - Built-in theme switching
- Responsive Design - Mobile-first with Tailwind CSS v4
| Feature | Implementation |
|---|---|
| Product browsing | Next.js pages with CoCart-backed product/category data |
| Product search & filters | Server-side with URL params |
| Shopping cart | Client-side with localStorage, synced to CoCart session |
| Checkout form | Native, on /checkout — no redirect to WooCommerce |
| Payment processing | Handled in-app via CoCart Plus's checkout API (Stripe Elements, offline gateways, redirect-based gateways) |
| Account management | Native login, registration, and account dashboard (orders, downloads, profile) |
| Order confirmation | Next.js success page |
| Blog | WordPress posts via REST API |
Instead of redirecting to WooCommerce's hosted checkout and My Account:
- Control - Full ownership of the checkout and account UX, no jarring domain switch
- Flexibility - Store owner can still change payment gateways in WooCommerce admin without code changes
- Still secure - Card details go straight to the gateway (e.g. Stripe Elements); WooCommerce/CoCart Plus still creates and owns the order
- Battle-tested backend - Order creation, payment, and account data still live in WooCommerce/CoCart, just surfaced natively
- WordPress 6.0+ with HTTPS enabled
- WooCommerce 8.0+ installed and activated
- CoCart plugin installed and activated (powers the cart/session REST endpoints used by
lib/cocart.ts) - Node.js 18+ and pnpm
If you don't have a WordPress site yet:
- Hosting: Use any WordPress host (WP Engine, Bluehost, Cloudways, etc.) or local development (Local by Flywheel, MAMP, Docker)
- Install WordPress: Follow your host's WordPress installation process
- Enable HTTPS: Required for WooCommerce API authentication
Important: The REST API requires pretty permalinks.
- Go to Settings → Permalinks
- Select Post name (or any option except "Plain")
- Click Save Changes
- Install WooCommerce: Plugins → Add New → Search "WooCommerce"
- Activate and run the setup wizard
- Configure your store basics (currency, location, etc.)
This app never renders WooCommerce's own Shop/Cart templates — it has its own /shop and /cart routes. But WooCommerce still needs its internal Shop and Cart pages registered (wc_get_page_id()), since some core behavior (URL generation, redirects, certain REST/webhook logic) depends on them existing. WooCommerce creates these automatically on install; just verify they're still there:
- Shop - Product listing (internal reference only; the storefront's product listing lives at this app's
/shop) - Cart - Shopping cart (internal reference only; the storefront's cart lives at this app's
/cart)
Check in WooCommerce → Settings → Advanced → Page Setup.
Checkout and My Account are different: this project replaces WooCommerce's hosted Checkout/My Account pages entirely with a native CoCart Plus-powered flow (/checkout, /account, /login, /register). There's no equivalent WooCommerce page to keep around for those — skip them in Page Setup.
- Go to Products → Add New
- Add product title, description, price, and images
- Set stock status and publish
- Repeat or import products via Products → Import
- Go to WooCommerce → Settings → Advanced → REST API
- Click Add Key
- Set Description to "Next.js" (or any label)
- Set User to an admin account
- Set Permissions to Read/Write
- Click Generate API Key
- Copy both keys immediately - the secret is only shown once
git clone https://github.com/cocart-headless/next-woo.git
cd next-woo
pnpm install
cp .env.example .env.localEdit .env.local with your credentials:
# WordPress/WooCommerce Site
WORDPRESS_URL="https://your-wordpress-site.com"
WORDPRESS_HOSTNAME="your-wordpress-site.com"
NEXT_PUBLIC_WORDPRESS_URL="https://your-wordpress-site.com"
# Webhook secret (generate with: openssl rand -base64 32)
WORDPRESS_WEBHOOK_SECRET="your-secret-key-here"
# WooCommerce API credentials from Step 3
WC_CONSUMER_KEY="ck_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
WC_CONSUMER_SECRET="cs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# Only needed if you enable the WooCommerce Stripe Gateway - mounts Stripe
# Elements client-side for card payments (safe to expose)
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="pk_test_xxxxxxxxxxxxxxxxxxxxxxxx"Configure your payment gateway in WooCommerce → Settings → Payments. Checkout is handled by this Next.js app via CoCart Plus's checkout API — no redirect to WooCommerce.
Popular options:
- Stripe (WooCommerce Stripe Gateway) - Card payments via Stripe Elements, mounted client-side; requires
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY - PayPal or other redirect-based gateways - Customer is sent to
action_data.redirectand returns to/checkout/success - Cash on Delivery - For testing, no client-side config needed
For the customer to land back on this Next.js site after payment:
- Set CoCart Starter's
frontend_urlsetting in WP admin sopayment_result.redirect_urlpoints at this app instead of the WooCommerce domain - Redirect-based gateways send the customer to
/checkout/successautomatically once payment completes
For automatic cache updates when products/posts change:
- Download the plugin from wordpress/next-revalidate in this repo
- Go to Plugins → Add New → Upload Plugin
- Upload and activate the plugin
- Go to Settings → Next.js Revalidation
- Enter your Next.js site URL and webhook secret
pnpm devYour site is now running at http://localhost:3000.
- Visit
http://localhost:3000/shop- Should display your products - Visit
http://localhost:3000/posts- Should display your blog posts - Test the cart and checkout flow
┌─────────────────┐ ┌─────────────────┐
│ │ │ │
│ Next.js │ ◄─────► │ WooCommerce │
│ (Frontend) │ API │ (Backend) │
│ │ │ │
└────────┬────────┘ └────────┬────────┘
│ │
│ ┌───────────────────────┐ │
│ │ User Flow │ │
│ └───────────────────────┘ │
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ Browse │ │ Account │
│ Cart │ ────API─────► │ Orders │
│ Checkout │ │ │
└──────────┘ └──────────┘
(Next.js) (WooCommerce)
Checkout is native/headless via CoCart Plus's checkout API — payment for supported gateways (offline gateways plus any redirect-based gateway) is handled without leaving the Next.js app:
- Customer adds items to cart (CoCart session, cart-key based)
- Customer fills the billing form on
/checkout; address/shipping-rate calculation happens live viacart-provider.tsx - Customer selects a payment method (
GET checkout/payment-methods) - Order is created and paid in one call (
POST checkoutvialib/cocart-checkout.ts'sprocessCheckout()) - The response's
payment_result.payment_statusdetermines what happens next:success/no_payment_required/on_holdredirect toredirect_url(this app's own/checkout/successif CoCart Starter'sfrontend_urlis configured in WP admin, otherwise the WooCommerce domain); arequires_actionredirect gateway sends the customer toaction_data.redirect - Cart cleared automatically before redirecting
next-woo/
├── app/
│ ├── api/
│ │ ├── og/ # OG image generation
│ │ └── revalidate/ # Cache revalidation webhook
│ ├── shop/ # Product listing
│ ├── product/[slug]/ # Product detail pages
│ ├── product-category/[slug]/ # Category pages
│ ├── cart/ # Shopping cart page
│ ├── checkout/
│ │ └── success/ # Order confirmation
│ ├── account/
│ │ ├── (dashboard)/ # Profile, orders, order detail, downloads
│ │ └── verify-email/ # Email verification
│ ├── login/ # Customer login
│ ├── register/ # Customer registration
│ ├── posts/ # Blog posts
│ └── pages/ # WordPress pages
├── components/
│ ├── shop/ # Shop components
│ ├── posts/ # Blog components
│ ├── ui/ # shadcn/ui components
│ └── theme/ # Theme toggle
├── lib/
│ ├── woocommerce.ts # Orders, customers, coupons, shipping, payment gateways
│ ├── woocommerce.d.ts # WooCommerce type definitions
│ ├── cocart.ts # CoCart SDK-backed product/category/cart functions
│ ├── cocart-client.ts # Shared CoCart SDK client singleton
│ ├── cocart-checkout.ts # CoCart Plus native checkout API client
│ ├── cocart-account.ts # CoCart Plus account/auth API client
│ ├── cocart-register.ts # Customer registration API client
│ ├── stripe-client.ts # Stripe Elements client-side helper
│ ├── wordpress.ts # WordPress API functions
│ └── wordpress.d.ts # WordPress type definitions
├── site.config.ts # Site metadata
└── menu.config.ts # Navigation configuration
import { getProducts, getProductBySlug } from "@/lib/cocart";
// Get paginated products
const { data: products, headers } = await getProducts(1, 12, {
category: 5,
on_sale: true,
orderby: "price",
});
// Get single product
const product = await getProductBySlug("product-name");import { getAllProductCategories, getProductCategoryBySlug } from "@/lib/cocart";
const categories = await getAllProductCategories();
const category = await getProductCategoryBySlug("clothing");import { getPaymentMethods, processCheckout } from "@/lib/cocart-checkout";
const methods = await getPaymentMethods();
const { payment_result } = await processCheckout({
billing_address: { email: "[email protected]", first_name: "...", /* ... */ },
shipping_address: { /* ... */ },
use_different_billing: true,
payment_method: "cod",
});
if (payment_result.payment_status === "success") {
window.location.href = payment_result.redirect_url;
}import { useCart } from "@/components/shop";
function MyComponent() {
const { cart, addItem, removeItem, updateQuantity, clearCart } = useCart();
await addItem({
productId: 123,
quantity: 1,
name: "Product Name",
price: "29.99",
});
}Edit site.config.ts:
export const siteConfig = {
site_name: "Your Store",
site_domain: "https://yourstore.com",
site_description: "Your store description",
};Edit menu.config.ts:
export const mainMenu = {
home: "/",
shop: "/shop",
blog: "/posts",
};pnpm dev # Start development server
pnpm build # Build for production
pnpm start # Start production server
pnpm lint # Run ESLint- Verify WooCommerce REST API credentials are correct
- Check API at
your-site.com/wp-json/wc/v3/products - Ensure products are published and visible
- Add WordPress domain to
WORDPRESS_HOSTNAME - Check
next.config.tshas correctremotePatterns
- Verify CoCart Plus is active and its
checkout/configendpoint responds - Set CoCart Starter's
frontend_urlsetting in WP admin sopayment_result.redirect_urlpoints back at this Next.js app instead of the WooCommerce domain - Check the selected payment gateway is enabled and configured in WooCommerce
- Verify CoCart Plus's account/auth endpoints respond (
lib/cocart-account.ts) - Ensure
WC_CONSUMER_KEY/WC_CONSUMER_SECRETare valid — order history on/account/ordersuses the WooCommerce REST API - Ensure
NEXT_PUBLIC_WORDPRESS_URLis set correctly — the client-side CoCart SDK (lib/cocart-client.ts) uses it as its base URL
- Next.js 16 - React framework
- React 19 - UI library
- Tailwind CSS v4 - Styling
- shadcn/ui - UI components
- WooCommerce - E-commerce backend
- CoCart - Headless cart & REST API for WooCommerce
MIT License
Built on a forked next-wp by 9d8.
Headless cart functionality powered by CoCart.