> ## Documentation Index
> Fetch the complete documentation index at: https://docs.craveup.com/llms.txt
> Use this file to discover all available pages before exploring further.

# React Native / Expo Quickstart

> Build a mobile ordering app with React Native and the Crave Storefront SDK.

Build a native mobile ordering app using React Native (Expo) and `@craveup/storefront-sdk`. The SDK is framework-agnostic and works anywhere `fetch` is available — including React Native.

## Prerequisites

* Node.js 18+ and a package manager
* Expo CLI (`npx expo`)
* A Crave API key from the [Dashboard](https://craveup.com/login)
* At least one live location in your merchant account

## 1. Create an Expo project

```bash theme={null}
npx create-expo-app my-storefront --template blank-typescript
cd my-storefront
```

## 2. Install the SDK

<Snippet file="sdk-install.mdx" />

## 3. Set environment variables

Create a `.env` file and install `expo-constants` to access them:

```env filename=".env" theme={null}
EXPO_PUBLIC_CRAVEUP_API_KEY=sk_live_your_api_key
EXPO_PUBLIC_LOCATION_ID=loc_your_location_id
EXPO_PUBLIC_LOCATION_SLUG=your-restaurant-slug
```

<Tip>
  Expo exposes variables prefixed with `EXPO_PUBLIC_` at build time via `process.env`.
</Tip>

## 4. Create the storefront client

```ts filename="src/lib/storefront.ts" theme={null}
import { createStorefrontClient } from '@craveup/storefront-sdk';

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

React Native includes a global `fetch` implementation, so no polyfill is needed.

## 5. Display the merchant

```tsx filename="src/screens/HomeScreen.tsx" theme={null}
import { useEffect, useState } from 'react';
import { View, Text, FlatList, StyleSheet } from 'react-native';
import { storefront } from '../lib/storefront';
import type { MerchantApiResponse } from '@craveup/storefront-sdk';

const SLUG = process.env.EXPO_PUBLIC_LOCATION_SLUG!;

export default function HomeScreen() {
  const [merchant, setMerchant] = useState<MerchantApiResponse | null>(null);

  useEffect(() => {
    storefront.merchant.getBySlug(SLUG).then(setMerchant);
  }, []);

  if (!merchant) return <Text style={styles.loading}>Loading...</Text>;

  return (
    <View style={styles.container}>
      <Text style={styles.title}>{merchant.name}</Text>
      <Text style={styles.bio}>{merchant.bio}</Text>

      <FlatList
        data={merchant.locations}
        keyExtractor={(loc) => loc.id}
        renderItem={({ item }) => (
          <View style={styles.card}>
            <Text style={styles.cardTitle}>{item.restaurantDisplayName}</Text>
            <Text style={styles.cardSub}>{item.addressString}</Text>
          </View>
        )}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 24 },
  loading: { padding: 24, textAlign: 'center' },
  title: { fontSize: 28, fontWeight: 'bold' },
  bio: { fontSize: 16, color: '#666', marginTop: 8 },
  card: { padding: 16, borderWidth: 1, borderColor: '#ddd', borderRadius: 8, marginTop: 12 },
  cardTitle: { fontSize: 18, fontWeight: '600' },
  cardSub: { fontSize: 14, color: '#888', marginTop: 4 },
});
```

## 6. Create a cart hook

```ts filename="src/hooks/useCart.ts" theme={null}
import { useState, useCallback } from 'react';
import { storefront } from '../lib/storefront';
import type { StorefrontCart } from '@craveup/storefront-sdk';

const LOCATION_ID = process.env.EXPO_PUBLIC_LOCATION_ID!;

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

  const startSession = useCallback(async () => {
    const session = await storefront.orderingSessions.start(LOCATION_ID, {
      marketplaceId: LOCATION_ID,
    });
    if (session.cartId) setCartId(session.cartId);
    return session.cartId;
  }, []);

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

  const refreshCart = useCallback(async () => {
    if (!cartId) return;
    const updated = await storefront.cart.get(LOCATION_ID, cartId);
    setCart(updated);
  }, [cartId]);

  return { cart, cartId, addItem, refreshCart, startSession };
}
```

## 7. Add-to-cart screen

```tsx filename="src/screens/OrderScreen.tsx" theme={null}
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import { useCart } from '../hooks/useCart';

export default function OrderScreen() {
  const { cart, addItem } = useCart();

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Order</Text>

      <TouchableOpacity style={styles.button} onPress={() => addItem('prod_margherita')}>
        <Text style={styles.buttonText}>Add Margherita Pizza</Text>
      </TouchableOpacity>

      {cart && (
        <View style={styles.cartSummary}>
          <Text style={styles.cartText}>
            {cart.totalQuantity} items — {cart.orderTotalWithServiceFeeFormatted}
          </Text>
        </View>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 24 },
  title: { fontSize: 24, fontWeight: 'bold' },
  button: { marginTop: 16, backgroundColor: '#0d9488', padding: 14, borderRadius: 8, alignItems: 'center' },
  buttonText: { color: '#fff', fontSize: 16, fontWeight: '600' },
  cartSummary: { marginTop: 24, padding: 16, borderWidth: 1, borderColor: '#ddd', borderRadius: 8 },
  cartText: { fontSize: 16 },
});
```

## 8. Run on a device

```bash theme={null}
npx expo start
```

Scan the QR code with the Expo Go app on your phone, or press `i` for the iOS simulator / `a` for the Android emulator.

## API key security on mobile

<Warning>
  Mobile apps ship API keys to the device. For production apps, proxy API requests through your own backend so keys never leave your server. The SDK's `baseUrl` option lets you point at your proxy instead of `api.craveup.com` directly.
</Warning>

## Next steps

<CardGroup cols={2}>
  <Card title="Display Menu" icon="utensils" href="/guides/display-menu">
    Fetch menus, categories, and products for a location.
  </Card>

  <Card title="Manage Cart" icon="cart-shopping" href="/guides/manage-cart">
    Add, update, and remove items with modifiers.
  </Card>

  <Card title="Fulfillment Methods" icon="truck" href="/guides/fulfillment-methods">
    Set up delivery, table-side, and room service.
  </Card>

  <Card title="SDK Reference" icon="code" href="/getting-started/storefront-sdk">
    Full SDK documentation with all available modules.
  </Card>
</CardGroup>

<Snippet file="need-help.mdx" />
