Skip to main content
Build a fully functional ordering storefront using Next.js 15 (App Router) and @craveup/storefront-sdk. By the end of this guide you will fetch menus, display products, and add items to a cart.

Prerequisites

  • Node.js 18+ and a package manager (pnpm, npm, or yarn)
  • A Crave API key from the Dashboard
  • At least one live location in your merchant account

1. Create a Next.js project

npx create-next-app@latest my-storefront --typescript --app --tailwind
cd my-storefront

2. Install the SDK

npm install @craveup/storefront-sdk

3. Set environment variables

Create a .env.local file in the root of your project with the following variables:
NEXT_PUBLIC_CRAVEUP_API_KEY=sk_live_your_api_key
NEXT_PUBLIC_LOCATION_ID=loc_your_location_id
NEXT_PUBLIC_LOCATION_SLUG=your-restaurant-slug
VariableDescription
NEXT_PUBLIC_CRAVEUP_API_KEYLocation-scoped API key from the Dashboard
NEXT_PUBLIC_LOCATION_IDThe location ID to load menus and accept orders for
NEXT_PUBLIC_LOCATION_SLUGHuman-readable slug used for merchant lookups
Use different API keys per environment (development, staging, production). The SDK reads NEXT_PUBLIC_CRAVEUP_API_KEY by default.

4. Create the storefront client

Create a shared client instance that every server and client component can import.
import { createStorefrontClient } from '@craveup/storefront-sdk';

export const storefront = createStorefrontClient({
  apiKey: process.env.NEXT_PUBLIC_CRAVEUP_API_KEY!,
});

5. Fetch merchant data (Server Component)

Use a React Server Component to load the merchant and its locations at request time.
import { storefront } from '@/lib/storefront';

export default async function HomePage() {
  const merchant = await storefront.merchant.getBySlug(
    process.env.NEXT_PUBLIC_LOCATION_SLUG!
  );

  return (
    <main className="max-w-4xl mx-auto p-8">
      <h1 className="text-3xl font-bold">{merchant.name}</h1>
      <p className="text-gray-600 mt-2">{merchant.bio}</p>

      <h2 className="text-xl font-semibold mt-8">Locations</h2>
      <ul className="mt-4 space-y-3">
        {merchant.locations.map((loc) => (
          <li key={loc.id} className="p-4 border rounded-lg">
            <p className="font-medium">{loc.restaurantDisplayName}</p>
            <p className="text-sm text-gray-500">{loc.addressString}</p>
          </li>
        ))}
      </ul>
    </main>
  );
}

6. Start an ordering session and display the cart

Create a Client Component that starts a session and lets users add items.
'use client';

import { useEffect, useState } from 'react';
import { storefront } from '@/lib/storefront';
import type { StorefrontCart } from '@craveup/storefront-sdk';

const LOCATION_ID = process.env.NEXT_PUBLIC_LOCATION_ID!;

export default function OrderPage() {
  const [cart, setCart] = useState<StorefrontCart | null>(null);
  const [cartId, setCartId] = useState<string | null>(null);

  useEffect(() => {
    storefront.orderingSessions
      .start(LOCATION_ID, { marketplaceId: LOCATION_ID })
      .then((session) => {
        if (session.cartId) setCartId(session.cartId);
      });
  }, []);

  async function addItem(productId: string) {
    if (!cartId) return;
    const result = await storefront.cart.addItem(LOCATION_ID, cartId, {
      productId,
      quantity: 1,
      selections: [],
      itemUnavailableAction: 'remove_item',
    });
    setCart(result.cart);
  }

  return (
    <main className="max-w-4xl mx-auto p-8">
      <h1 className="text-2xl font-bold">Order</h1>
      <button
        onClick={() => addItem('prod_margherita')}
        className="mt-4 px-4 py-2 bg-teal-600 text-white rounded"
      >
        Add Margherita Pizza
      </button>
      {cart && (
        <div className="mt-6 p-4 border rounded-lg">
          <p className="font-medium">Cart ({cart.totalQuantity} items)</p>
          <p className="text-lg mt-1">Total: {cart.orderTotalWithServiceFeeFormatted}</p>
        </div>
      )}
    </main>
  );
}

7. Run the dev server

pnpm dev
Open http://localhost:3000 to see your merchant info, and navigate to /order to test the cart.

Next steps