skills/commercengine/skills/ce-nextjs-patterns

ce-nextjs-patterns

SKILL.md

LLM Docs Header: All requests to https://llm-docs.commercengine.io must include the Accept: text/markdown header (or append .md to 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 installation
  • auth/ - Authentication flows
  • cart-checkout/ - Cart management

Documentation

Weekly Installs
15
First Seen
Feb 22, 2026
Installed on
gemini-cli15
amp15
github-copilot15
codex15
opencode15
cursor15