ce-nextjs-patterns
SKILL.md
LLM Docs Header: All requests to
https://llm-docs.commercengine.iomust include theAccept: text/markdownheader (or append.mdto the URL path). Without it, responses return HTML instead of parseable markdown.
Next.js Patterns
For basic setup, see setup/.
Impact Levels
- CRITICAL - Breaking bugs, security holes
- HIGH - Common mistakes
- MEDIUM - Optimization
References
| Reference | Impact |
|---|---|
references/server-vs-client.md |
CRITICAL - storefront(cookies()) vs storefront() |
references/token-management.md |
HIGH - Cookie-based token flow in Next.js |
Mental Model
The storefront() function adapts to the execution context:
| Context | Usage | Token Storage |
|---|---|---|
| Client Components | storefront() |
Browser cookies |
| Server Components | storefront(cookies()) |
Request cookies |
| Server Actions | storefront(cookies()) |
Request cookies (read + write) |
| Root Layout | storefront({ isRootLayout: true }) |
Memory fallback |
| Build time (SSG) | storefront() |
Memory (no user context) |
Setup
1. Install
npm install @commercengine/storefront-sdk-nextjs
2. Create Config
// lib/storefront.ts
export { storefront } from "@commercengine/storefront-sdk-nextjs";
3. Root Layout
// app/layout.tsx
import { StorefrontSDKInitializer } from "@commercengine/storefront-sdk-nextjs/client";
import { storefront } from "@/lib/storefront";
// Root Layout has no request context — use isRootLayout flag
const sdk = storefront({ isRootLayout: true });
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<StorefrontSDKInitializer />
{children}
</body>
</html>
);
}
4. Environment Variables
# .env.local
NEXT_PUBLIC_STORE_ID=your-store-id
NEXT_PUBLIC_API_KEY=your-api-key
NEXT_BUILD_CACHE_TOKENS=true # Faster builds with token caching
Key Patterns
Server Component (Data Fetching)
// app/products/page.tsx
import { storefront } from "@/lib/storefront";
import { cookies } from "next/headers";
export default async function ProductsPage() {
const sdk = storefront(cookies());
const { data, error } = await sdk.catalog.listProducts({
page: 1, limit: 20,
});
if (error) return <p>Error: {error.message}</p>;
return (
<div>
{data.products.map((product) => (
<div key={product.id}>{product.name}</div>
))}
</div>
);
}
Server Actions (Mutations)
// app/actions.ts
"use server";
import { storefront } from "@/lib/storefront";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
export async function loginWithEmail(email: string) {
const sdk = storefront(cookies());
const { data, error } = await sdk.auth.loginWithEmail({
email,
register_if_not_exists: true,
});
if (error) return { error: error.message };
return { otp_token: data.otp_token, otp_action: data.otp_action };
}
export async function verifyOtp(otp: string, otpToken: string, otpAction: string) {
const sdk = storefront(cookies());
const { data, error } = await sdk.auth.verifyOtp({
otp,
otp_token: otpToken,
otp_action: otpAction,
});
if (error) return { error: error.message };
redirect("/account");
}
export async function addToCart(cartId: string, productId: string, variantId: string | null) {
const sdk = storefront(cookies());
const { data, error } = await sdk.cart.addDeleteCartItem(
{ id: cartId },
{ product_id: productId, variant_id: variantId, quantity: 1 }
);
if (error) return { error: error.message };
return { cart: data.cart };
}
Static Site Generation (SSG)
// app/products/[slug]/page.tsx
import { storefront } from "@/lib/storefront";
// Pre-render product pages at build time
export async function generateStaticParams() {
const sdk = storefront(); // No cookies at build time
const { data } = await sdk.catalog.listProducts({ limit: 100 });
return (data?.products ?? []).map((product) => ({
slug: product.slug,
}));
}
export default async function ProductPage({ params }: { params: { slug: string } }) {
const sdk = storefront(); // No cookies for static pages
const { data, error } = await sdk.catalog.getProductDetail({
product_id_or_slug: params.slug,
});
if (error) return <p>Product not found</p>;
const product = data.product;
return (
<div>
<h1>{product.name}</h1>
<p>{product.selling_price}</p>
{/* AddToCartButton is a Client Component */}
</div>
);
}
Client Component
"use client";
import { storefront } from "@/lib/storefront";
export function AddToCartButton({ productId, variantId }: Props) {
async function handleClick() {
const sdk = storefront(); // No cookies in client components
const { data, error } = await sdk.cart.addDeleteCartItem(
{ id: cartId },
{ product_id: productId, variant_id: variantId, quantity: 1 }
);
}
return <button onClick={handleClick}>Add to Cart</button>;
}
SEO Metadata
Use Next.js generateMetadata with CE product fields for meta tags, Open Graph, and structured data:
// app/products/[slug]/page.tsx
import { storefront } from "@/lib/storefront";
import type { Metadata } from "next";
export async function generateMetadata({ params }: { params: { slug: string } }): Promise<Metadata> {
const sdk = storefront(); // No cookies — metadata runs at build time for static pages
const { data } = await sdk.catalog.getProductDetail({
product_id_or_slug: params.slug,
});
const product = data?.product;
if (!product) return { title: "Product Not Found" };
const image = product.images?.[0];
return {
title: product.name,
description: product.short_description,
openGraph: {
title: product.name,
description: product.short_description ?? undefined,
images: image ? [{ url: image.url_standard, alt: image.alternate_text ?? product.name }] : [],
},
};
}
CE field → meta tag mapping:
| Meta Tag | CE Field |
|---|---|
<title> |
product.name |
meta description |
product.short_description |
og:image |
product.images[0].url_standard |
og:image:alt |
product.images[0].alternate_text |
| Canonical URL | Build from product.slug |
For category/PLP pages, use the category name and description from
listCategories(). For search pages, use the search query.
Common Pitfalls
| Level | Issue | Solution |
|---|---|---|
| CRITICAL | Missing cookies() in Server Components |
Use storefront(cookies()) for user-specific data on the server |
| CRITICAL | Auth in Server Components instead of Actions | Auth endpoints that return tokens MUST be in Server Actions, not Server Components |
| HIGH | Missing StorefrontSDKInitializer |
Required in root layout for automatic anonymous auth and session continuity |
| HIGH | Using cookies() in Client Components |
Client Components use storefront() (no cookies) — tokens managed via browser cookies |
| MEDIUM | Slow builds | Set NEXT_BUILD_CACHE_TOKENS=true for token caching during SSG |
| MEDIUM | Root Layout missing isRootLayout flag |
Root Layout runs outside request context — use storefront({ isRootLayout: true }) |
See Also
setup/- Basic SDK installationauth/- Authentication flowscart-checkout/- Cart management
Documentation
- Next.js Integration: https://www.commercengine.io/docs/sdk/nextjs-integration
- Token Management: https://www.commercengine.io/docs/sdk/token-management
- LLM Reference: https://llm-docs.commercengine.io/sdk/
Weekly Installs
15
Repository
commercengine/skillsFirst Seen
Feb 22, 2026
Security Audits
Installed on
gemini-cli15
amp15
github-copilot15
codex15
opencode15
cursor15