Skip to main content

CommerceEngine Next.js SDK

Next.js wrapper for the CommerceEngine Storefront SDK. Provides the perfect developer experience with automatic context detection, universal API, and zero configuration complexity.

Installation

npm install @commercengine/storefront-sdk-nextjs

Quick Start

1

Environment Variables (Required)

Add your store configuration to .env.local:
NEXT_PUBLIC_STORE_ID=your-store-id
NEXT_PUBLIC_API_KEY=your-api-key
# Environment (defaults to "staging")
NEXT_PUBLIC_ENVIRONMENT=staging  # or "production"
Environment variables are mandatory - the SDK will throw helpful errors if missing
2

Create Your Storefront Configuration

Create lib/storefront.ts in your project:
// lib/storefront.ts
import { storefront } from "@commercengine/storefront-sdk-nextjs";

// Re-export for easy importing everywhere
export { storefront };

// Import any types you need
export type { 
  UserInfo,
  SupportedDefaultHeaders,
  components,
  operations 
} from "@commercengine/storefront-sdk-nextjs";
The storefront function works with minimal configuration - environment variables handle everything automatically
3

Initialize in Root Layout

// app/layout.tsx
import { StorefrontSDKInitializer } from "@commercengine/storefront-sdk-nextjs/client";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <StorefrontSDKInitializer />
        {children}
      </body>
    </html>
  );
}
The StorefrontSDKInitializer is required for automatic anonymous token creation and user session continuity
4

Use Anywhere with Universal Import

// Import your configured storefront everywhere
import { storefront } from "@/lib/storefront";
import { cookies } from "next/headers"; // Only for server contexts

// ✅ Client Component - No cookies needed
const products = await storefront().catalog.listProducts();

// ✅ Server Component, Server Action, or API Route - MUST pass cookies
const products = await storefront(cookies()).catalog.listProducts();

// ✅ Root Layout - Special exception with explicit flag
const products = await storefront({ isRootLayout: true }).catalog.listProducts();

// ✅ SSG/ISR (build contexts) - Automatic fallback to memory storage
// (NEXT_BUILD_CACHE_TOKENS=true enables this)
const products = await storefront().catalog.listProducts();
The SDK enforces proper cookie passing in server contexts to protect user session continuity and analytics tracking

Usage in Different Next.js Contexts

Client Components

Client Components run in the browser and can persist tokens via cookies:
// components/ProductList.tsx
"use client";

import { useState, useEffect } from "react";
import { storefront } from "@/lib/storefront";

export default function ProductList() {
  const [products, setProducts] = useState([]);
  
  useEffect(() => {
    async function loadProducts() {
      const sdk = storefront(); // No cookies() needed on client-side
      
      // Tokens are automatically managed by StorefrontSDKInitializer
      const { data } = await sdk.catalog.listProducts();
      if (data) setProducts(data.products);
    }
    
    loadProducts();
  }, []);

  return (
    <div>
      {products.map(product => (
        <div key={product.id}>{product.name}</div>
      ))}
    </div>
  );
}

Server Components

Server Components run on the server and can read cookies:
// 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: products } = await sdk.catalog.listProducts();

  return (
    <div>
      {products?.products.map(product => (
        <div key={product.id}>{product.name}</div>
      ))}
    </div>
  );
}

Server Actions

Server Actions can both read and write cookies, perfect for authentication:
// app/actions.ts
"use server";

import { storefront } from "@/lib/storefront";
import { cookies } from "next/headers";

export async function loginWithEmail(email: string, password: string) {
  const sdk = storefront(cookies());
  
  const { data, error } = await sdk.auth.loginWithPassword({ email, password });
  
  if (data) {
    // Tokens are automatically saved to cookies
    return { success: true, user: data.user };
  }
  
  return { success: false, error: error?.message };
}

API Routes

API Routes work identically to Server Actions:
// app/api/products/route.ts
import { storefront } from "@/lib/storefront";
import { cookies } from "next/headers";
import { NextResponse } from "next/server";

export async function GET() {
  try {
    const sdk = storefront(cookies());
    const { data, error } = await sdk.catalog.listProducts();

    if (error) {
      return NextResponse.json({ error: error.message }, { status: 400 });
    }
    
    return NextResponse.json(data);
  } catch (error) {
    return NextResponse.json(
      { error: "Internal server error" }, 
      { status: 500 }
    );
  }
}

Root Layout (Special Case)

Root Layout requires explicit flag since it’s outside request context. Passing this flag will instruct the SDK to fallback to MemoryStore when rendering the root layout as the root layout runs on both server and client before cookie context is available.
// app/layout.tsx
import { StorefrontSDKInitializer } from "@commercengine/storefront-sdk-nextjs/client";
import { storefront } from "@/lib/storefront";

// Root Layout requires explicit flag - no request context available
const sdk = storefront({ isRootLayout: true });
const { data: storeConfig } = await sdk.store.getStoreConfig();

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <StorefrontSDKInitializer />
        <h1>Welcome to {storeConfig?.store_config?.brand.name}</h1>
        {children}
      </body>
    </html>
  );
}

Static Site Generation (SSG) & Build-Time Optimization

The SDK provides powerful build-time optimizations through intelligent token caching.

Configure Next.js for Build-Time Optimization

Create or update your next.config.ts (or next.config.js) to enable automatic token caching during builds:
// next.config.ts
import type { NextConfig } from "next";
import { PHASE_PRODUCTION_BUILD } from 'next/constants';

const nextConfig = (phase: string): NextConfig => {
  const isBuild = phase === PHASE_PRODUCTION_BUILD;
  
  return {
    env: {
      // Enable build-time token caching during production builds and when explicitly set
      NEXT_BUILD_CACHE_TOKENS: process.env.NEXT_BUILD_CACHE_TOKENS ?? (isBuild ? 'true' : 'false'),
      // Critical: tells SDK to use MemoryStore during SSG/Build/ISR to avoid cookie context failures
      NEXT_IS_BUILD: isBuild ? 'true' : 'false',
    },
    
    // Ensure static generation is enabled
    output: undefined, // Let Next.js decide based on usage
  };
};

export default nextConfig;

SSG with generateStaticParams

// app/products/[slug]/page.tsx
import { storefront } from "@/lib/storefront";
import { notFound } from "next/navigation";

interface ProductPageProps {
  params: Promise<{ slug: string }>;
}

export default async function ProductPage({ params }: ProductPageProps) {
  const { slug } = await params;
  const sdk = storefront(); // No cookies() - uses build-time storage
  
  const { data, error } = await sdk.catalog.getProductDetail({
    product_id_or_slug: slug
  });
  
  if (error || !data) {
    notFound();
  }

  return (
    <div>
      <h1>{data.product.name}</h1>
      <p>SKU: {data.product.sku}</p>
      <p>Description: {data.product.short_description}</p>
    </div>
  );
}

// Generate static params from real API data
export async function generateStaticParams() {
  const sdk = storefront(); // Token will be cached and reused
  
  const { data: productsData, error } = await sdk.catalog.listProducts({
    limit: 100
  });
  
  if (error || !productsData) {
    return [];
  }
  
  return productsData.products.map(product => ({
    slug: product.slug || product.id
  }));
}

Build Performance Benefits

With token caching enabled:
  • ✅ Token created once and reused across all pages
  • ✅ 100 pages = ~1 anonymous token API call total
  • ✅ Faster builds, dramatically lower API usage

Authentication Patterns

⚠️ Important: Any authentication endpoint that returns tokens (like loginWithPassword, verifyOtp, register, etc.) must be called in contexts where cookies can be set and managed:
  • Server Actions (recommended for authentication flows)
  • API Routes (/api directory)
  • Client Components (browser environment)
  • Server Components (cannot set cookies, tokens won’t persist)
This ensures the SDK can automatically handle token storage and user session continuity.

Anonymous Users

The SDK automatically creates anonymous tokens via the StorefrontSDKInitializer component imported in your root layout. If this component is not imported (not recommended), a new token will be minted for each request as a fallback. It is highly recommended to use the StorefrontSDKInitializer and ensure all token-returning endpoints are called from request contexts that can set cookies.
// This works everywhere - creates anonymous token automatically
const { data: products } = await storefront(cookies()).catalog.listProducts();

Email/Password Login

// Server Action (✅ Recommended - can set cookies for token persistence)
"use server";
export async function loginUser(email: string, password: string) {
  const sdk = storefront(cookies());
  
  const { data, error } = await sdk.auth.loginWithPassword({ email, password });
  
  if (data) {
    // Tokens automatically saved to cookies
    redirect('/dashboard');
  }
  
  return { error: error?.message };
}

Phone/OTP Login

// Server Action - Step 1: Send OTP (✅ Recommended context)
export async function sendOTP(phone: string, country_code: string) {
  const sdk = storefront(await cookies());
  
  return await sdk.auth.loginWithPhone({
    phone,
    country_code,
    register_if_not_exists: true
  });
}

// Server Action - Step 2: Verify OTP (✅ Recommended - can set cookies for token persistence)
export async function verifyOTP(otp: string, otp_token: string, otp_action: string) {
  const sdk = storefront(await cookies());
  
  const { data, error } = await sdk.auth.verifyOtp({
    otp,
    otp_token,
    otp_action
  });
  
  if (data) {
    // Tokens automatically saved to cookies
    return { success: true, user: data.user };
  }
  
  return { success: false, error: error?.message };
}

API Usage Patterns

The storefront() function works across all Next.js contexts:
// Client-side (browser)
const sdk = storefront();

// Server-side (requires cookies for user continuity)  
const sdk = storefront(cookies());

// Root Layout (special exception with explicit flag)
const sdk = storefront({ isRootLayout: true });

Error Handling

All SDK methods return a consistent error structure:
const { data, error, response } = await storefront(cookies()).catalog.listProducts();

if (error) {
  console.error("API Error:", error.message, error.code);
  console.log("Status:", response.status);
} else {
  console.log("Products:", data.products);
}

Best Practices

Environment Variables

Set required environment variables: NEXT_PUBLIC_STORE_ID and NEXT_PUBLIC_API_KEY are mandatory for proper initialization

Configuration

Create one lib/storefront.ts file to import and re-export the storefront function for use throughout your app

Universal Import

Use storefront(cookies()) for server contexts and storefront() for client contexts with the universal API pattern

Authentication Context

Use Server Actions for authentication flows that need to persist tokens and ensure user session continuity

Error Handling

Always check the error property in responses and implement proper error boundaries for graceful failure handling

Build Optimization

Enable NEXT_BUILD_CACHE_TOKENS=true for dramatically faster builds with token caching during SSG generation

Troubleshooting

“Server context requires cookies for user continuity!”
// ❌ Wrong - server context without cookies (breaks user sessions)
storefront() 
   
// ✅ Correct - server context with cookies
storefront(cookies())
   
// ✅ Root Layout exception
storefront({ isRootLayout: true })
# Ensure your .env.local has the required variables:
NEXT_PUBLIC_STORE_ID=your-store-id  
NEXT_PUBLIC_API_KEY=your-api-key
Symptoms: Initialization errors, token creation failures Fix: Verify environment variables are set and accessible
“Cookie store passed in client environment”
// ❌ Wrong - client component with cookies
storefront(cookies())
   
// ✅ Correct - client component without cookies  
storefront()

Migration Guide

From Core SDK

// Before (core SDK)
import { StorefrontSDK } from "@commercengine/storefront-sdk";
const sdk = new StorefrontSDK({ 
  storeId: "...", 
  tokenStorage: new BrowserTokenStorage() 
});

// After (Next.js SDK)  
import { storefront } from "@/lib/storefront";
const sdk = storefront(cookies());

Benefits of Migration

  • Zero token management complexity - SDK handles everything automatically
  • Universal imports - Same storefront() pattern everywhere
  • Better performance - Request isolation and token caching
  • Production reliability - Proper cookie handling across all contexts

Why This Pattern?

The Perfect DX Pattern provides:
  • One Config File: Simple lib/storefront.ts setup
  • Environment Variables: Basic config (storeId, apiKey) via env vars
  • Universal Import: Same storefront() import everywhere
  • Automatic Management: Handles all token complexity automatically
  • Clear Patterns: Simple usage for different Next.js contexts
  • Zero Configuration: Works out of the box with just environment variables
Result: Production-ready e-commerce with zero token management complexity! 🛡️

Complete API Reference

For complete API documentation of all available endpoints, visit SDK API Reference The Next.js SDK provides access to all the same endpoints as the core SDK:
  • sdk.auth.* - Authentication and user management
  • sdk.customer.* - Customer profiles and preferences
  • sdk.catalog.* - Products, categories, and search
  • sdk.cart.* - Shopping cart management
  • sdk.order.* - Order creation and tracking
  • sdk.shipping.* - Shipping methods and rates
  • sdk.helpers.* - Countries, currencies, utilities

Cross-References

SDK Client Overview

Understanding the core SDK client patterns and architecture

Authentication Guide

Complete authentication patterns for storefront implementations

Storefront Token Management

Advanced token management and refresh token patterns
This Next.js adapter pattern ensures your authentication works consistently across Next.js server components, client components, API routes, and server actions with zero complexity for token management.
I