npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@commercengine/storefront-sdk-nextjs

v0.2.5

Published

Next.js wrapper for the Commerce Engine Storefront SDK

Readme

CommerceEngine Next.js SDK

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

Built on @commercengine/ssr-utils for robust server-side token management.

✨ Perfect DX Pattern:

  • 🎯 One Config File - Single lib/storefront.ts using createStorefront()
  • 🌍 Universal API - Same storefront() import works everywhere
  • 🔥 Automatic Tokens - Creates and manages tokens automatically
  • 🧠 Smart Context Detection - Detects Server vs Client vs Build contexts
  • 🍪 Cookie-based State - Shared authentication via Next.js cookies
  • ⚡ Request Isolation - Proper per-request SDK instances on server
  • 🛠 Zero Complexity - Environment variables + one lib file = done

Installation

npm install @commercengine/storefront-sdk-nextjs
# or
pnpm add @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"

2. Create Your Storefront Configuration

Create lib/storefront.ts in your project:

// lib/storefront.ts
import {
  createStorefront,
  type StorefrontRuntimeConfig,
} from "@commercengine/storefront-sdk-nextjs";

// Optional advanced configuration (everything not in environment variables)
const storefrontConfig: StorefrontRuntimeConfig = {
  debug: true,
  timeout: 15000,
  logger: (msg: string, ...args: any[]) =>
    console.log("[STOREFRONT]", msg, ...args),
  onTokensUpdated: (access: string, refresh: string) => {
    console.log("🔥 TOKENS UPDATED:", {
      access: access.slice(0, 20) + "...",
      refresh: refresh.slice(0, 20) + "...",
    });
  },
  onTokensCleared: () => {
    console.log("🔄 TOKENS CLEARED");
  },
};

// Create the configured storefront function (storefrontConfig is OPTIONAL)
export const storefront = createStorefront(storefrontConfig);

// Re-export types for convenience
export type { StorefrontRuntimeConfig };

All Core SDK Types Available: The Next.js SDK re-exports all types from the core SDK, so you can import them directly:

// Import any core SDK types you need
import type { 
  UserInfo,
  SupportedDefaultHeaders,
  StorefrontSDKOptions,
  components,
  operations 
} from "@commercengine/storefront-sdk-nextjs";

// Use imported types
const headers: SupportedDefaultHeaders = {
  customer_group_id: "01ABC123..."
};

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>
  );
}

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();

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. Authentication Patterns.

// 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 Reference

createStorefront(config?)

Creates a configured storefront function that works universally across all Next.js contexts.

import { createStorefront } from "@commercengine/storefront-sdk-nextjs";

export const storefront = createStorefront({
  debug: true,
  logger: (msg, ...args) => console.log('[DEBUG]', msg, ...args)
});

Parameters:

  • config (optional): StorefrontRuntimeConfig - Advanced configuration options

Returns:

  • Universal storefront() function that works in all Next.js contexts

StorefrontRuntimeConfig

Optional configuration object for createStorefront():

interface StorefrontRuntimeConfig {
  // Override environment variables
  storeId?: string;
  apiKey?: string;
  environment?: Environment;
  baseUrl?: string;
  timeout?: number;
  debug?: boolean;
  
  // Advanced options (not available via environment variables)
  accessToken?: string;
  refreshToken?: string;
  defaultHeaders?: SupportedDefaultHeaders;
  logger?: (message: string, ...args: any[]) => void;
  onTokensUpdated?: (accessToken: string, refreshToken: string) => void;
  onTokensCleared?: () => void;
  tokenStorageOptions?: NextJSTokenStorageOptions;
}

Universal storefront() Function

The function returned by createStorefront() works in all contexts with strict enforcement:

// 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 });

// Server-side without cookies - throws helpful error to protect user sessions
const sdk = storefront(); // ❌ Throws error in server contexts

Function Signatures

function storefront(): StorefrontSDK;
function storefront(cookieStore: NextCookieStore): StorefrontSDK;
function storefront(options: { isRootLayout: true }): StorefrontSDK;
function storefront(cookieStore: NextCookieStore, options: { isRootLayout?: boolean }): StorefrontSDK;

StorefrontSDKInitializer

Client-side initializer component (must be imported from /client):

import { StorefrontSDKInitializer } from "@commercengine/storefront-sdk-nextjs/client";

<StorefrontSDKInitializer />

No props needed - configuration comes from environment variables and your lib/storefront.ts file.

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

✅ Do's

  • Set required environment variables: NEXT_PUBLIC_STORE_ID and NEXT_PUBLIC_API_KEY are mandatory
  • Create one lib/storefront.ts file: Use createStorefront() to configure advanced options
  • Initialize once: Call StorefrontSDKInitializer only in your root layout
  • Use storefront(cookies()): Always pass cookies in server contexts
  • Handle errors: Always check the error property in responses
  • Use Server Actions: For authentication flows that need to persist tokens

❌ Don'ts

  • Don't call cookies() in Client Components: It will throw an error
  • Don't initialize multiple times: The SDK handles singleton behavior
  • Don't forget error handling: API calls can fail for various reasons
  • Don't skip environment variables: Missing required variables will cause errors

Troubleshooting

Common Issues

  1. "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 })
  2. Missing Environment Variables

    # Ensure your .env.local has the required variables:
    NEXT_PUBLIC_STORE_ID=your-store-id  
    NEXT_PUBLIC_API_KEY=your-api-key
  3. "Cookie store passed in client environment"

    // ❌ Wrong - client component with cookies
    storefront(cookies())
       
    // ✅ Correct - client component without cookies  
    storefront()
  4. Server Actions and async cookies()

    // ✅ Correct - Server Actions need await cookies()
    const sdk = storefront(await cookies());
       
    // ✅ Server Components and API Routes use cookies() directly  
    const sdk = storefront(cookies());

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());

Why This Pattern?

The Perfect DX Pattern provides:

  • One Config File: All advanced configuration in lib/storefront.ts
  • Environment Variables: Basic config (storeId, apiKey) via env vars
  • Universal Import: Same storefront() import everywhere
  • Strict User Continuity: Enforces cookie passing to protect user sessions and analytics
  • Explicit Exceptions: Clear patterns for special cases like Root Layout
  • Zero Guesswork: Helpful errors guide developers to correct patterns

Result: Production-ready e-commerce with bulletproof user session management! 🛡️

API Reference

For complete API documentation of all available endpoints, visit SDK DOCS 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.payments.* - Payment methods and processing
  • sdk.store.* - Store configuration
  • sdk.helpers.* - Countries, currencies, utilities

Related Packages

License

All Rights Reserved