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

# React Native Code Examples for the Tappd Mobile SDK

> Copy-paste React Native examples for e-commerce, authentication, push notifications, screen tracking, context providers, and more with the Tappd SDK.

The examples below cover the most common integration patterns you'll encounter when adding Tappd to a React Native app. Each section is self-contained and copy-pasteable. Expand whichever scenario matches your use case and adapt the code to fit your app structure.

<Accordion title="E-commerce — Product Purchase Flow">
  Track the full purchase funnel from product view through to order completion. Use Tappd's standard e-commerce event names so your data maps cleanly to built-in dashboard reports.

  ```javascript theme={null}
  import TappdSDK from '@tappd/mobile-sdk';

  const tappd = new TappdSDK({ appId: 'YOUR_APP_ID' });

  // 1. User views a product
  await tappd.track('ecommerce.product_viewed', {
    productId: 'prod_123',
    productName: 'iPhone 15',
    category: 'Electronics',
    price: 999.00,
    currency: 'USD'
  });

  // 2. User adds item to cart
  await tappd.track('ecommerce.add_to_cart', {
    productId: 'prod_123',
    productName: 'iPhone 15',
    quantity: 1,
    price: 999.00,
    currency: 'USD'
  });

  // 3. User begins checkout
  await tappd.track('ecommerce.checkout_started', {
    cartValue: 999.00,
    currency: 'USD'
  });

  // 4. User completes purchase
  await tappd.track('ecommerce.purchase', {
    orderId: 'ord_456',
    total: 999.00,
    currency: 'USD',
    items: [{
      productId: 'prod_123',
      name: 'iPhone 15',
      price: 999.00,
      quantity: 1
    }]
  });
  ```
</Accordion>

<Accordion title="User Authentication — Sign Up, Login, and Logout">
  Track every step of the authentication flow and link anonymous pre-signup activity to the newly created account.

  **Sign up flow:**

  ```javascript theme={null}
  async function handleSignUp(email, name) {
    // Track signup attempt
    await tappd.track('user.signup.started', {
      method: 'email'
    });

    try {
      // Create user account in your backend
      const user = await createAccount(email, name);

      // Identify the user — merges any pre-signup anonymous events
      await tappd.identify({
        external_id: user.id,
        email: email,
        name: name,
        signupDate: new Date().toISOString()
      });

      // Track success — no identifier needed, SDK associates automatically
      await tappd.track('user.signup.completed', {
        method: 'email'
      });
    } catch (error) {
      await tappd.track('user.signup.failed', {
        method: 'email',
        error: error.message
      });
    }
  }
  ```

  **Login flow:**

  ```javascript theme={null}
  async function handleLogin(email) {
    await tappd.track('user.login.started', { method: 'email' });

    try {
      const user = await authenticate(email);

      await tappd.identify({
        external_id: user.id,
        email: user.email,
        name: user.name,
        lastLogin: new Date().toISOString()
      });

      await tappd.track('user.login.completed', { method: 'email' });
    } catch (error) {
      await tappd.track('user.login.failed', {
        method: 'email',
        error: error.message
      });
    }
  }
  ```

  **Logout — reset the SDK to start a new anonymous session:**

  ```javascript theme={null}
  function handleLogout() {
    tappd.track('user.logout');

    // Clears user data and begins a fresh anonymous session
    tappd.reset();
  }
  ```
</Accordion>

<Accordion title="Screen Tracking — React Navigation and Manual">
  **With React Navigation (`useFocusEffect`):**

  Use `useFocusEffect` so the screen view fires every time a screen comes into focus, not just on initial mount.

  ```javascript theme={null}
  import React from 'react';
  import { View, Text } from 'react-native';
  import { useFocusEffect } from '@react-navigation/native';
  import { useTappd } from '../contexts/TappdContext';

  function ProductScreen({ route }) {
    const tappd = useTappd();
    const { productId } = route.params;

    useFocusEffect(
      React.useCallback(() => {
        tappd.trackScreen('ProductScreen', {
          productId: productId,
          category: 'ecommerce'
        });
      }, [productId])
    );

    return (
      <View>
        <Text>Product Screen</Text>
      </View>
    );
  }
  ```

  **Manual tracking with `useEffect`:**

  Use this approach for screens outside React Navigation's stack.

  ```javascript theme={null}
  import { useEffect } from 'react';
  import { useTappd } from '../contexts/TappdContext';

  function MyScreen() {
    const tappd = useTappd();

    useEffect(() => {
      tappd.trackScreen('MyScreen', {
        category: 'main',
        section: 'dashboard'
      });
    }, []);

    return (
      // Screen content
    );
  }
  ```
</Accordion>

<Accordion title="Push Notifications — Complete Setup">
  This example covers the full push notification lifecycle: requesting permissions, getting the FCM token, registering it with Tappd, handling token refreshes, and tracking notification opens.

  ```javascript theme={null}
  import React, { useEffect } from 'react';
  import messaging from '@react-native-firebase/messaging';
  import { Platform } from 'react-native';
  import TappdSDK from '@tappd/mobile-sdk';

  const tappd = new TappdSDK({ appId: 'YOUR_APP_ID' });

  function App() {
    useEffect(() => {
      setupPushNotifications();
    }, []);

    async function setupPushNotifications() {
      try {
        // Request permission (iOS only)
        if (Platform.OS === 'ios') {
          const authStatus = await messaging().requestPermission();
          const enabled =
            authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
            authStatus === messaging.AuthorizationStatus.PROVISIONAL;

          if (!enabled) return;
        }

        // Get the FCM token
        const token = await messaging().getToken();

        if (token) {
          // User must be identified before registering a push token
          await tappd.identify({
            external_id: 'user_123',
            email: 'john@example.com'
          });

          await tappd.registerPushToken(
            token,
            Platform.OS === 'ios' ? 'ios' : 'android'
          );
        }

        // Re-register whenever the token refreshes
        messaging().onTokenRefresh(async (newToken) => {
          await tappd.registerPushToken(
            newToken,
            Platform.OS === 'ios' ? 'ios' : 'android'
          );
        });

        // Track foreground message receipts
        messaging().onMessage(async (remoteMessage) => {
          await tappd.track('push_notification.received', {
            notificationId: remoteMessage.messageId,
            title: remoteMessage.notification?.title
          });
        });

        // Track notification-driven app opens (warm start)
        messaging().onNotificationOpenedApp(async (remoteMessage) => {
          await tappd.trackPushOpen(remoteMessage);
        });

        // Track notification-driven app opens (cold start)
        const initialMessage = await messaging().getInitialNotification();
        if (initialMessage) {
          await tappd.trackPushOpen(initialMessage);
        }
      } catch (error) {
        console.error('Push setup failed:', error);
      }
    }

    return (
      // Your app
    );
  }
  ```
</Accordion>

<Accordion title="Subscription Management — Upgrade and Cancellation">
  Track plan changes and update the customer profile in one flow.

  **Subscription upgrade:**

  ```javascript theme={null}
  async function handleUpgrade(newPlan, userId) {
    const currentPlan = getUserPlan();

    await tappd.track('subscription.upgrade', {
      fromPlan: currentPlan,
      toPlan: newPlan,
      upgradeDate: new Date().toISOString()
    });

    // external_id must match the one used in identify()
    await tappd.setUserAttributes({
      external_id: userId,
      plan: newPlan,
      upgradedAt: new Date().toISOString()
    });
  }
  ```

  **Subscription cancellation:**

  ```javascript theme={null}
  async function handleCancellation(reason, userId) {
    await tappd.track('subscription.cancelled', {
      reason: reason,
      cancelledAt: new Date().toISOString()
    });

    await tappd.setUserAttributes({
      external_id: userId,
      planStatus: 'cancelled',
      cancellationDate: new Date().toISOString()
    });
  }
  ```
</Accordion>

<Accordion title="App Lifecycle — Foreground, Background, and Cleanup">
  Track when the app moves between states and clean up the SDK when it goes to the background.

  ```javascript theme={null}
  import { AppState } from 'react-native';
  import { useEffect } from 'react';
  import { useTappd } from '../contexts/TappdContext';

  function App() {
    const tappd = useTappd();

    useEffect(() => {
      const subscription = AppState.addEventListener('change', (nextAppState) => {
        if (nextAppState === 'active') {
          tappd.track('app.foreground');
        } else if (nextAppState === 'background') {
          tappd.track('app.background');
          tappd.cleanup(); // Flush pending events before backgrounding
        }
      });

      return () => {
        subscription.remove();
      };
    }, []);

    return (
      // Your app
    );
  }
  ```
</Accordion>

<Accordion title="Button Tracking — Reusable Tracked Button Component">
  Wrap a `TouchableOpacity` in a reusable component that tracks every press automatically.

  ```javascript theme={null}
  import { TouchableOpacity, Text } from 'react-native';
  import { useTappd } from '../contexts/TappdContext';

  function MyButton({ buttonId, title, onPress }) {
    const tappd = useTappd();

    const handlePress = async () => {
      await tappd.track('button_click', {
        buttonId: buttonId,
        buttonText: title,
        location: 'home_screen'
      });

      if (onPress) onPress();
    };

    return (
      <TouchableOpacity onPress={handlePress}>
        <Text>{title}</Text>
      </TouchableOpacity>
    );
  }

  // Usage:
  // <MyButton
  //   buttonId="signup_button"
  //   title="Sign Up"
  //   onPress={() => navigation.navigate('SignUp')}
  // />
  ```
</Accordion>

<Accordion title="Form Tracking — Field Focus and Submission Events">
  Track individual field interactions and form submissions to understand where users drop off in your sign-up funnel.

  ```javascript theme={null}
  import { useState } from 'react';
  import { TextInput, Button } from 'react-native';
  import { useTappd } from '../contexts/TappdContext';

  function SignUpForm() {
    const tappd = useTappd();
    const [formData, setFormData] = useState({});

    const handleFieldFocus = async (fieldName) => {
      await tappd.track('form.field_focused', {
        formId: 'signup_form',
        fieldName: fieldName
      });
    };

    const handleSubmit = async () => {
      await tappd.track('form.submitted', {
        formId: 'signup_form',
        formName: 'Sign Up',
        fieldsCompleted: Object.keys(formData).length
      });

      await submitForm(formData);
    };

    return (
      <>
        <TextInput
          placeholder="Email"
          onFocus={() => handleFieldFocus('email')}
          onChangeText={(text) => setFormData({ ...formData, email: text })}
        />
        <Button title="Submit" onPress={handleSubmit} />
      </>
    );
  }
  ```
</Accordion>

<Accordion title="Context Provider Pattern — Recommended Architecture">
  The recommended way to share the SDK instance across your app is a React Context. Create the provider once at the root, then consume it with the `useTappd` hook in any component.

  **Create the context (`contexts/TappdContext.js`):**

  ```javascript theme={null}
  import React, { createContext, useContext, useState, useEffect } from 'react';
  import TappdSDK from '@tappd/mobile-sdk';

  const TappdContext = createContext(null);

  export function TappdProvider({ children, appId }) {
    const [tappd] = useState(() => {
      return new TappdSDK({
        appId: appId,
        debug: __DEV__
      });
    });

    useEffect(() => {
      return () => {
        tappd.cleanup();
      };
    }, []);

    return (
      <TappdContext.Provider value={tappd}>
        {children}
      </TappdContext.Provider>
    );
  }

  export function useTappd() {
    const context = useContext(TappdContext);
    if (!context) {
      throw new Error('useTappd must be used within TappdProvider');
    }
    return context;
  }
  ```

  **Wrap your app root (`App.js`):**

  ```javascript theme={null}
  import { TappdProvider } from './contexts/TappdContext';

  function App() {
    return (
      <TappdProvider appId="YOUR_APP_ID">
        {/* Your app components */}
      </TappdProvider>
    );
  }
  ```
</Accordion>

<Accordion title="Error Tracking — Global React Native Error Handler">
  Capture unhandled JavaScript errors and fatal crashes with React Native's global error handler, then forward them to Tappd for analysis.

  ```javascript theme={null}
  import { ErrorUtils } from 'react-native';
  import TappdSDK from '@tappd/mobile-sdk';

  const tappd = new TappdSDK({ appId: 'YOUR_APP_ID' });

  ErrorUtils.setGlobalHandler(async (error, isFatal) => {
    try {
      await tappd.track('error', {
        message: error.message,
        stack: error.stack,
        isFatal: isFatal,
        timestamp: new Date().toISOString()
      });
    } catch (trackingError) {
      console.error('Failed to track error:', trackingError);
    }

    // Preserve the default error behaviour
    if (ErrorUtils.getGlobalHandler) {
      ErrorUtils.getGlobalHandler()(error, isFatal);
    }
  });
  ```
</Accordion>

<Accordion title="Deep Linking — Track Cold and Warm Starts">
  Track every deep link open and distinguish between cold starts (app was not running) and warm starts (app was already in memory).

  ```javascript theme={null}
  import { Linking } from 'react-native';
  import { useEffect } from 'react';
  import { useTappd } from '../contexts/TappdContext';

  function App() {
    const tappd = useTappd();

    useEffect(() => {
      // Handle the link that launched the app from a closed state
      Linking.getInitialURL().then((url) => {
        if (url) {
          tappd.track('deep_link.opened', {
            url: url,
            source: 'cold_start'
          });
        }
      });

      // Handle links when the app is already running
      const subscription = Linking.addEventListener('url', (event) => {
        tappd.track('deep_link.opened', {
          url: event.url,
          source: 'warm_start'
        });
      });

      return () => {
        subscription.remove();
      };
    }, []);

    return (
      // Your app
    );
  }
  ```
</Accordion>
