Skip to main content
This guide covers how to load a restaurant’s menu data and display it in your storefront — including multiple menus, category filtering, and product details.

How menus work

Every Crave location has one or more menus. Each menu contains categories, and each category links to products. The active menu depends on the current time and the location’s schedule.
Merchant → Location → Menu(s) → Categories → Products → Modifiers

Fetch location data

The location response includes basic metadata. Menu and product data comes from the ordering session and API endpoints.
import { storefront } from '@/lib/storefront';

const location = await storefront.locations.getById('loc_123');
console.log(location.restaurantDisplayName);

Start a session to get the menu

Starting an ordering session gives you a cartId and loads the menu for the current time. The menus, categories, and products are returned as part of the location’s data model.
const session = await storefront.orderingSessions.start('loc_123', {
  marketplaceId: 'loc_123',
});

// The session creates a cart; use the location endpoint
// to fetch categories and products
const location = await storefront.locations.getById('loc_123');

Display categories

Categories are used to group products (e.g., “Appetizers”, “Mains”, “Drinks”). Render them as tabs or a sidebar.
interface Category {
  id: string;
  name: string;
  productIds: string[];
}

function CategoryTabs({
  categories,
  activeId,
  onSelect,
}: {
  categories: Category[];
  activeId: string;
  onSelect: (id: string) => void;
}) {
  return (
    <div className="flex gap-2 overflow-x-auto">
      {categories.map((cat) => (
        <button
          key={cat.id}
          onClick={() => onSelect(cat.id)}
          className={`px-4 py-2 rounded-full text-sm whitespace-nowrap ${
            cat.id === activeId
              ? 'bg-teal-600 text-white'
              : 'bg-gray-100 text-gray-700'
          }`}
        >
          {cat.name}
        </button>
      ))}
    </div>
  );
}

Display products

Each product includes a name, description, price, images, and modifier groups.
interface Product {
  id: string;
  name: string;
  description: string;
  price: string;
  displayPrice: string;
  images: string[];
}

function ProductCard({ product, onAdd }: { product: Product; onAdd: () => void }) {
  return (
    <div className="flex gap-4 p-4 border rounded-lg">
      {product.images[0] && (
        <img
          src={product.images[0]}
          alt={product.name}
          className="w-20 h-20 rounded-lg object-cover"
        />
      )}
      <div className="flex-1">
        <h3 className="font-medium">{product.name}</h3>
        <p className="text-sm text-gray-500 line-clamp-2">{product.description}</p>
        <div className="flex justify-between items-center mt-2">
          <span className="font-semibold">{product.displayPrice}</span>
          <button
            onClick={onAdd}
            className="px-3 py-1 bg-teal-600 text-white rounded text-sm"
          >
            Add
          </button>
        </div>
      </div>
    </div>
  );
}
Crave menus are returned as structured data. You can implement client-side filtering:
// Filter products by category
const categoryProducts = products.filter((p) =>
  activeCategory.productIds.includes(p.id)
);

// Search by name
const searchResults = products.filter((p) =>
  p.name.toLowerCase().includes(query.toLowerCase())
);

Get order times

Fetch the available time slots for ASAP or scheduled orders.
const orderTimes = await storefront.locations.getOrderTimes('loc_123');

if (orderTimes.requireScheduledOrders) {
  // Show day/time picker from orderTimes.orderDays
} else {
  // ASAP is available; optionally show schedule option
  console.log('Schedule allowed:', orderTimes.scheduleAllowed);
}

Product types

The SDK exports these types for working with menu data:
TypeDescription
MenuTop-level menu with id, name, isActive, and categories
CategoryGrouping with id, name, and productIds
ProductFull product with price, images, modifiers, and nutrition
ModifierModifier group with selection rules and items
ModifierItemIndividual modifier option with price and max quantity

Next steps