> ## 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.

# Screen Tracking with React Navigation for React Native

> Integrate Tappd with React Navigation to automatically track screen views, navigation events, and deep links across your React Native app.

Tappd works seamlessly alongside React Navigation — the most popular navigation library for React Native — to give you automatic screen-level analytics without wiring up tracking calls on every screen manually. You can use a lightweight hook-based approach for individual screens, or set up a centralized navigation tracker that fires on every route change.

## Installation

If you haven't already installed React Navigation, add the core library, the native stack navigator, and the required peer dependencies:

```bash theme={null}
npm install @react-navigation/native @react-navigation/native-stack
npm install react-native-screens react-native-safe-area-context
```

## Basic Setup

### Track screens with `useFocusEffect`

The quickest way to track a screen is to call `tappd.trackScreen()` inside the `useFocusEffect` hook. The hook fires every time the screen comes into focus, so you get an accurate view even when users navigate back to it.

```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 HomeScreen() {
  const tappd = useTappd();

  useFocusEffect(
    React.useCallback(() => {
      tappd.trackScreen('HomeScreen', {
        category: 'main',
        section: 'dashboard',
      });

      return () => {
        // Optional: run cleanup when the screen loses focus
      };
    }, [])
  );

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

## Navigation Container Setup

For a centralized approach that tracks every screen transition automatically, create a `useNavigationTracking` helper and attach it to your `NavigationContainer`.

### Create the navigation helper

```javascript theme={null}
// utils/navigationTracker.js
import { useEffect, useRef } from 'react';
import { useNavigationContainerRef } from '@react-navigation/native';

export function useNavigationTracking(tappd) {
  const navigationRef = useNavigationContainerRef();
  const routeNameRef = useRef();

  useEffect(() => {
    if (!navigationRef || !tappd) return;

    const onStateChange = async () => {
      const previousRouteName = routeNameRef.current;
      const currentRouteName = navigationRef.getCurrentRoute()?.name;

      if (previousRouteName !== currentRouteName) {
        await tappd.trackScreen(currentRouteName, {
          previousScreen: previousRouteName,
        });
      }

      routeNameRef.current = currentRouteName;
    };

    navigationRef.addListener('state', onStateChange);

    return () => {
      navigationRef.removeListener('state', onStateChange);
    };
  }, [navigationRef, tappd]);

  return navigationRef;
}
```

### Use the helper in `App.js`

```javascript theme={null}
// App.js
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { useTappd } from './contexts/TappdContext';
import { useNavigationTracking } from './utils/navigationTracker';

const Stack = createNativeStackNavigator();

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

  return (
    <NavigationContainer ref={navigationRef}>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Profile" component={ProfileScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}
```

## Advanced: Custom Navigator

If you want tracking baked into a reusable navigator component, wrap `createNativeStackNavigator` in a `TrackedStackNavigator` that listens for state changes internally.

```javascript theme={null}
// components/TrackedStackNavigator.js
import React from 'react';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { useNavigationContainerRef } from '@react-navigation/native';
import { useTappd } from '../contexts/TappdContext';

const Stack = createNativeStackNavigator();

export function TrackedStackNavigator({ screens }) {
  const tappd = useTappd();
  const navigationRef = useNavigationContainerRef();
  const routeNameRef = React.useRef();

  React.useEffect(() => {
    if (!navigationRef || !tappd) return;

    const unsubscribe = navigationRef.addListener('state', async () => {
      const previousRouteName = routeNameRef.current;
      const currentRouteName = navigationRef.getCurrentRoute()?.name;

      if (previousRouteName !== currentRouteName) {
        await tappd.trackScreen(currentRouteName, {
          previousScreen: previousRouteName,
          timestamp: new Date().toISOString(),
        });
      }

      routeNameRef.current = currentRouteName;
    });

    return unsubscribe;
  }, [navigationRef, tappd]);

  return (
    <Stack.Navigator>
      {screens.map((screen) => (
        <Stack.Screen
          key={screen.name}
          name={screen.name}
          component={screen.component}
          options={screen.options}
        />
      ))}
    </Stack.Navigator>
  );
}
```

Use it just like the standard stack navigator:

```javascript theme={null}
<TrackedStackNavigator
  screens={[
    { name: 'Home', component: HomeScreen },
    { name: 'Profile', component: ProfileScreen },
  ]}
/>
```

## Tab Navigation

For bottom tab navigators, add `useFocusEffect` inside each tab screen so tracking fires when a user switches tabs:

```javascript theme={null}
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { useFocusEffect } from '@react-navigation/native';

const Tab = createBottomTabNavigator();

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

  useFocusEffect(
    React.useCallback(() => {
      tappd.trackScreen('HomeTab');
    }, [])
  );

  return <HomeScreen />;
}

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

  useFocusEffect(
    React.useCallback(() => {
      tappd.trackScreen('ProfileTab');
    }, [])
  );

  return <ProfileScreen />;
}

function TabNavigator() {
  return (
    <Tab.Navigator>
      <Tab.Screen name="HomeTab" component={HomeTab} />
      <Tab.Screen name="ProfileTab" component={ProfileTab} />
    </Tab.Navigator>
  );
}
```

## Tracking Navigation Events

Track explicit navigation actions — for example, when a user taps a button that takes them to another screen — by calling `tappd.track()` before calling `navigation.navigate()`:

```javascript theme={null}
import { useNavigation } from '@react-navigation/native';

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

  const handleNavigate = async (screenName, params) => {
    await tappd.track('navigation.navigated', {
      from: 'CurrentScreen',
      to: screenName,
      params: params,
    });

    navigation.navigate(screenName, params);
  };

  return (
    <Button title="Go to Profile" onPress={() => handleNavigate('Profile')} />
  );
}
```

## Deep Linking

The SDK automatically tracks `deep_link.opened` events. For additional context — such as distinguishing between cold-start and warm-start deep links — you can supplement automatic tracking with manual calls:

```javascript theme={null}
import { Linking } from 'react-native';

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

  useEffect(() => {
    // Cold start: app launched via deep link
    Linking.getInitialURL().then((url) => {
      if (url) {
        tappd.track('deep_link.handled', {
          url: url,
          source: 'cold_start',
        });
      }
    });

    // Warm start: app already running when deep link arrives
    const subscription = Linking.addEventListener('url', (event) => {
      tappd.track('deep_link.handled', {
        url: event.url,
        source: 'warm_start',
      });
    });

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

  return (
    // Your app
  );
}
```

## Screen Tracking with Params

Pass route parameters as tracking context to enrich your analytics data:

```javascript theme={null}
function ProductScreen({ route }) {
  const tappd = useTappd();
  const { productId } = route.params;

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

  return (
    // Screen content
  );
}
```

## Complete Example

<CodeGroup>
  ```javascript App.js theme={null}
  import React from 'react';
  import { NavigationContainer } from '@react-navigation/native';
  import { createNativeStackNavigator } from '@react-navigation/native-stack';
  import TappdSDK from '@tappd/mobile-sdk';
  import { TappdProvider } from './contexts/TappdContext';
  import HomeScreen from './screens/HomeScreen';
  import ProfileScreen from './screens/ProfileScreen';

  const Stack = createNativeStackNavigator();

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

  function App() {
    return (
      <TappdProvider value={tappd}>
        <NavigationContainer>
          <Stack.Navigator>
            <Stack.Screen name="Home" component={HomeScreen} />
            <Stack.Screen name="Profile" component={ProfileScreen} />
          </Stack.Navigator>
        </NavigationContainer>
      </TappdProvider>
    );
  }

  export default App;
  ```

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

  function HomeScreen() {
    const navigation = useNavigation();
    const tappd = useTappd();

    useFocusEffect(
      React.useCallback(() => {
        tappd.trackScreen('HomeScreen');
      }, [])
    );

    const handleNavigate = async () => {
      await tappd.track('navigation.button_pressed', {
        buttonId: 'go_to_profile',
        from: 'HomeScreen',
      });
      navigation.navigate('Profile');
    };

    return (
      <View>
        <Button title="Go to Profile" onPress={handleNavigate} />
      </View>
    );
  }

  export default HomeScreen;
  ```
</CodeGroup>

## API Reference

| Method              | Signature                                            | Returns         | Description                                                                                                                                 |
| ------------------- | ---------------------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `tappd.trackScreen` | `(screenName: string, properties?: EventProperties)` | `Promise<void>` | Records a screen view event. Pass `properties` to attach arbitrary metadata — route params, categories, or any context useful for analysis. |

The `EventProperties` type accepts any flat or nested object of strings, numbers, or booleans.

```javascript theme={null}
// Minimal call
await tappd.trackScreen('HomeScreen');

// With properties
await tappd.trackScreen('ProductScreen', {
  productId: 'abc123',
  category: 'electronics',
});
```

## Best Practices

<Tip>
  Follow these patterns to get the most reliable screen tracking data across your app:

  * **Use `useFocusEffect` over `useEffect`** — `useEffect` only fires on mount, while `useFocusEffect` fires every time the screen gains focus, giving you accurate revisit counts.
  * **Always track with context** — Pass relevant route params (product IDs, category names, etc.) so your analytics have enough detail to be actionable.
  * **Use consistent screen names** — Pick a naming convention and stick with it. Inconsistent names (e.g., `"home"` vs. `"HomeScreen"` vs. `"Home"`) fragment your funnel data.
  * **Track explicit navigation actions** — Supplementing automatic screen tracking with event-level tracking (e.g., `navigation.button_pressed`) gives you a complete picture of user intent.
  * **Handle deep links** — Even though the SDK auto-tracks `deep_link.opened`, adding source context (`cold_start` vs. `warm_start`) helps attribute acquisition correctly.
</Tip>
