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
| Variable | Description |
|---|
NEXT_PUBLIC_CRAVEUP_API_KEY | Location-scoped API key from the Dashboard |
NEXT_PUBLIC_LOCATION_ID | The location ID to load menus and accept orders for |
NEXT_PUBLIC_LOCATION_SLUG | Human-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
Open http://localhost:3000 to see your merchant info, and navigate to /order to test the cart.
Next steps