bigcommerce-commerce/skills/nextjs-patterns/SKILL.md
Build with Next.js for BigCommerce — App Router, Server/Client Components, data fetching, ISR, middleware, API routes, and Catalyst patterns. Use when building headless BigCommerce storefronts with Next.js.
npx skillsauth add orcaqubits/agentic-commerce-claude-plugins nextjs-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Fetch live docs:
https://nextjs.org/docs for Next.js documentationhttps://www.catalyst.dev/ for Catalyst-specific patternsnextjs app router data fetching patterns for current best practicesapp/
├── page.tsx # /
├── layout.tsx # Root layout
├── products/
│ ├── page.tsx # /products
│ └── [slug]/
│ └── page.tsx # /products/:slug
├── cart/
│ └── page.tsx # /cart
├── api/
│ └── webhooks/
│ └── route.ts # /api/webhooks (API route)
└── not-found.tsx # 404 page
| File | Purpose |
|------|---------|
| page.tsx | Route component |
| layout.tsx | Shared layout (persists across navigation) |
| loading.tsx | Loading UI (Suspense boundary) |
| error.tsx | Error boundary |
| not-found.tsx | 404 page |
| route.ts | API route handler |
| template.tsx | Re-rendered layout (no persistence) |
await async operations directlyMark with 'use client' directive:
useState, useEffect, etc.)// Server Component — fetches data
async function ProductPage({ params }: { params: { slug: string } }) {
const product = await getProduct(params.slug); // Server-side fetch
return (
<div>
<h1>{product.name}</h1>
<AddToCartButton productId={product.id} /> {/* Client component */}
</div>
);
}
// Client Component — handles interactivity
'use client';
function AddToCartButton({ productId }: { productId: number }) {
const [loading, setLoading] = useState(false);
const handleClick = async () => { /* add to cart */ };
return <button onClick={handleClick}>Add to Cart</button>;
}
async function ProductsPage() {
const products = await fetch(`${STORE_URL}/graphql`, {
method: 'POST',
headers: { Authorization: `Bearer ${STOREFRONT_TOKEN}` },
body: JSON.stringify({ query: PRODUCTS_QUERY }),
next: { revalidate: 300 }, // ISR: revalidate every 5 minutes
}).then(r => r.json());
return <ProductGrid products={products.data.site.products} />;
}
| Strategy | Use Case | Config |
|----------|----------|--------|
| Static | Rarely changing data | { cache: 'force-cache' } |
| ISR | Product/category pages | { next: { revalidate: 300 } } |
| Dynamic | Cart, checkout, account | { cache: 'no-store' } |
| On-Demand | After webhook events | revalidateTag('products') |
Trigger revalidation from webhooks:
// app/api/webhooks/route.ts
export async function POST(request: Request) {
const body = await request.json();
if (body.scope === 'store/product/updated') {
revalidateTag('products');
}
return Response.json({ revalidated: true });
}
// app/api/webhooks/orders/route.ts
export async function POST(request: Request) {
const body = await request.json();
// Verify webhook authenticity
// Process order event
return Response.json({ received: true });
}
Proxy BigCommerce API calls to hide credentials:
// app/api/products/route.ts
export async function GET() {
const response = await fetch(`${BC_API_URL}/v3/catalog/products`, {
headers: { 'X-Auth-Token': process.env.BC_ACCESS_TOKEN! },
});
const data = await response.json();
return Response.json(data);
}
// middleware.ts
export function middleware(request: NextRequest) {
const token = request.cookies.get('auth_token');
if (request.nextUrl.pathname.startsWith('/account') && !token) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
Route users to the correct channel based on locale or region.
import Image from 'next/image';
<Image
src={product.imageUrl}
alt={product.name}
width={500}
height={500}
priority={isAboveFold} // Preload for LCP images
/>
Configure remotePatterns in next.config.js for BigCommerce CDN domains.
# .env.local
BIGCOMMERCE_STORE_HASH=abc123
BIGCOMMERCE_ACCESS_TOKEN=xxx # Server-only (no NEXT_PUBLIC_ prefix)
NEXT_PUBLIC_STORE_URL=https://... # Available in browser
BIGCOMMERCE_STOREFRONT_TOKEN=yyy # Client-side GraphQL
Catalyst includes a typed GraphQL client:
client/queries/ directoryclient/mutations/ directoryCatalyst provides pre-built components:
'use client' only when neededNEXT_PUBLIC_ prefixnext/image for automatic optimizationloading.tsx or Suspenseerror.tsx boundariesFetch the Next.js documentation and Catalyst source for exact API, configuration options, and current patterns before implementing.
development
Build with Spree's headless Next.js storefront — the official `spree/storefront` repo (Next.js 16 App Router with Server Actions and Turbopack, React 19 Server Components, Tailwind CSS 4, TypeScript 5, `@spree/sdk`, Sentry), server-only auth (httpOnly JWT cookies + publishable key), MeiliSearch faceted catalog, one-page checkout with Apple/Google Pay/Klarna/Affirm/SEPA, multi-region market routing, GA4 + JSON-LD SEO, and Vercel/Docker deployment. Use when forking or customizing the storefront, or evaluating headless adoption.
tools
Build Spree extensions as Rails engines — gem scaffolding, `bin/rails g spree:extension`, mounting routes/migrations/assets, the modern `prepend` decorator pattern (`*_decorator.rb` with `self.prepended(base)`), generators (`spree:model_decorator`, `spree:controller_decorator`), the four customization surfaces in preference order (Events > Webhooks > Dependencies > Decorators), Spree::Dependencies for swapping service objects, gem release/versioning, and the deprecated Deface engine. Use when building a reusable Spree extension or adding non-trivial customization to an app.
development
Build with Spree's event bus and Webhooks 2.0 — `Spree::Events` publication, `Spree::Subscriber` DSL with `subscribes_to` and `on`, wildcard matching, lifecycle events (`{model}.created/.updated/.deleted` via `publishes_lifecycle_events`), the canonical event catalog (order.*, payment.*, shipment.*, product.*), Webhooks 2.0 endpoints, HMAC-SHA256 signing (`X-Spree-Webhook-Signature`), exponential-backoff retries, and Sidekiq job orchestration. Use when wiring event-driven business logic, building webhook consumers, or replacing ActiveSupport callback chains.
tools
Cross-cutting Spree development patterns — the customization preference hierarchy (Events > Webhooks > Dependencies > Decorators), `Spree::Dependencies` service-object swapping, the `_decorator.rb` + `prepend` + `self.prepended` idiom, idempotent subscribers and webhook receivers, multi-store scoping discipline, prefixed IDs, calculator polymorphism (shipping/promotion/tax share the base), service-object composition with `dry-monads` or simple results, why to avoid `class_eval` reopening and Deface, and Spree-on-Rails idioms (Hotwire/Turbo Stimulus, ActiveStorage, Action Cable, Sidekiq). Use when designing the architecture of a Spree extension or solving cross-cutting concerns.