Skip to content

React Native SDK ​

React Native SDK for event tracking, error monitoring, and logging in iOS and Android mobile applications. Built on top of logstyx-js-core, the React Native SDK provides mobile-specific features including an ErrorBoundary component and a Trackable component for touch event tracking.

Installation ​

bash
npm install github:devatlogstyx/logstyx-js-react-native#release

Quick Start ​

jsx
import logstyx from 'logstyx-js-react-native';
import { View, Text, TouchableOpacity } from 'react-native';

function App() {
  const logger = logstyx({
    projectId: 'your_project_id',
    appid: 'com.yourcompany.app',
    endpoint: 'https://your-domain.com/api/v1/logs',
    captureUncaught: true,
    captureUnhandledRejections: true
  });

  const { ErrorBoundary, Trackable } = logger;

  return (
    <ErrorBoundary logger={logger}>
      <View>
        <Trackable 
          event="press" 
          context={{ screen: 'home' }} 
          data={{ action: 'signup' }}>
          <TouchableOpacity>
            <Text>Get Started</Text>
          </TouchableOpacity>
        </Trackable>
      </View>
    </ErrorBoundary>
  );
}

App ID Required

The appid should be your app's bundle identifier (e.g., com.yourcompany.app for Android or your iOS bundle ID). This is already defined in your app configuration.

Configuration ​

logstyx(options) ​

Initializes the Logstyx SDK and returns the logger instance with React Native components.

Required Options ​

projectId (string) ​

Your Logstyx project identifier.

javascript
projectId: 'proj_abc123xyz'

appid (string) ​

Your app's bundle identifier. Use your existing Android package name or iOS bundle identifier.

Android: com.yourcompany.appname
iOS: com.yourcompany.appname

javascript
appid: 'com.yourcompany.myapp'

Finding Your Bundle ID

  • Android: Check android/app/build.gradle → applicationId
  • iOS: Check ios/YourApp.xcodeproj → Bundle Identifier

Optional Options ​

endpoint (string) ​

Your Logstyx API endpoint.

javascript
endpoint: 'https://logs.yourcompany.com/api/v1/logs'

captureUncaught (boolean) ​

Automatically capture uncaught JavaScript exceptions.

Default: false

javascript
captureUncaught: true

captureUnhandledRejections (boolean) ​

Automatically capture unhandled Promise rejections.

Default: false

javascript
captureUnhandledRejections: true

ErrorBoundary Component ​

React component that catches errors in child components and logs them to Logstyx. Prevents your entire app from crashing when an error occurs.

Basic Usage ​

jsx
import logstyx from 'logstyx-js-react-native';
import { View, Text } from 'react-native';

function App() {
  const logger = logstyx({
    projectId: 'proj_abc123',
    appid: 'com.example.app',
    endpoint: 'https://logs.example.com/api/v1/logs'
  });

  const { ErrorBoundary } = logger;

  return (
    <ErrorBoundary logger={logger}>
      <YourApp />
    </ErrorBoundary>
  );
}

With Custom Fallback UI ​

jsx
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';

function CustomFallbackUI({ error, errorInfo }) {
  return (
    <View style={styles.errorContainer}>
      <Text style={styles.errorTitle}>Something went wrong</Text>
      <Text style={styles.errorMessage}>
        {error?.message || 'An unknown error occurred'}
      </Text>
      <TouchableOpacity 
        style={styles.reloadButton}
        onPress={() => {
          // Reload app or navigate to safe screen
        }}>
        <Text style={styles.reloadText}>Reload App</Text>
      </TouchableOpacity>
    </View>
  );
}

const styles = StyleSheet.create({
  errorContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
    backgroundColor: '#fff'
  },
  errorTitle: {
    fontSize: 20,
    fontWeight: 'bold',
    marginBottom: 10,
    color: '#d32f2f'
  },
  errorMessage: {
    fontSize: 14,
    color: '#666',
    marginBottom: 20,
    textAlign: 'center'
  },
  reloadButton: {
    backgroundColor: '#2196F3',
    paddingHorizontal: 20,
    paddingVertical: 10,
    borderRadius: 5
  },
  reloadText: {
    color: '#fff',
    fontWeight: 'bold'
  }
});

function App() {
  const logger = logstyx({ projectId: 'proj_abc123', appid: 'com.example.app' });
  const { ErrorBoundary } = logger;

  return (
    <ErrorBoundary logger={logger} fallbackUI={CustomFallbackUI}>
      <YourApp />
    </ErrorBoundary>
  );
}

Multiple Error Boundaries ​

Use multiple ErrorBoundary components to isolate errors to specific screens:

jsx
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

const Stack = createStackNavigator();

function App() {
  const logger = logstyx({ projectId: 'proj_abc123', appid: 'com.example.app' });
  const { ErrorBoundary } = logger;

  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="Home">
          {() => (
            <ErrorBoundary logger={logger}>
              <HomeScreen />
            </ErrorBoundary>
          )}
        </Stack.Screen>
        
        <Stack.Screen name="Profile">
          {() => (
            <ErrorBoundary logger={logger}>
              <ProfileScreen />
            </ErrorBoundary>
          )}
        </Stack.Screen>
      </Stack.Navigator>
    </NavigationContainer>
  );
}

Props ​

PropTypeRequiredDescription
loggerobjectYesThe logger instance from logstyx()
fallbackUIcomponentNoCustom error UI component
childrennodeYesChild components to protect

Trackable Component ​

Declarative component for tracking touch interactions in React Native.

Basic Usage ​

jsx
import logstyx from 'logstyx-js-react-native';
import { TouchableOpacity, Text } from 'react-native';

function MyComponent() {
  const logger = logstyx({ 
    projectId: 'proj_abc123', 
    appid: 'com.example.app' 
  });
  
  const { Trackable } = logger;

  return (
    <Trackable event="press" data={{ action: 'button_press' }}>
      <TouchableOpacity>
        <Text>Press Me</Text>
      </TouchableOpacity>
    </Trackable>
  );
}

With Context ​

Add persistent context that applies to the event:

jsx
<Trackable 
  event="press" 
  context={{ screen: 'checkout', step: 'payment' }}
  data={{ method: 'credit_card', amount: 99.99 }}>
  <TouchableOpacity>
    <Text>Complete Purchase</Text>
  </TouchableOpacity>
</Trackable>

Multiple Events ​

Track multiple touch events on a single element:

jsx
<Trackable 
  event="press longPress"
  context={{ component: 'product_card' }}
  data={{ productId: 'prod_123' }}>
  <TouchableOpacity>
    <Text>Product Name</Text>
  </TouchableOpacity>
</Trackable>

Custom Log Level ​

jsx
<Trackable 
  event="press" 
  level="warning"
  data={{ action: 'delete_account' }}>
  <TouchableOpacity>
    <Text>Delete Account</Text>
  </TouchableOpacity>
</Trackable>

Supported Events ​

React Native touch events that can be tracked:

  • press - When user taps the element
  • longPress - When user press and holds
  • pressIn - When touch starts
  • pressOut - When touch ends
jsx
<Trackable 
  event="press longPress pressIn pressOut"
  data={{ button: 'cta' }}>
  <TouchableOpacity>
    <Text>Interactive Button</Text>
  </TouchableOpacity>
</Trackable>

Props ​

PropTypeRequiredDefaultDescription
eventstringYes-Space-separated touch events
contextobjectNo{}Context metadata
dataobjectNo{}Event-specific data
levelstringNo"info"Log level: "info", "warning", "error", "critical"
childrennodeYes-Single child component

Single Child Required

Trackable must have exactly one child component. Wrap multiple components in a View if needed.

Manual Logging ​

Use the logger instance directly for manual logging:

Log Levels ​

jsx
import { useEffect } from 'react';

function MyScreen() {
  const logger = logstyx({ 
    projectId: 'proj_abc123', 
    appid: 'com.example.app' 
  });

  useEffect(() => {
    logger.info({ 
      message: 'Screen mounted', 
      screen: 'HomeScreen' 
    });

    return () => {
      logger.info({ 
        message: 'Screen unmounted', 
        screen: 'HomeScreen' 
      });
    };
  }, []);

  const handleAction = async () => {
    try {
      logger.info({ message: 'Action started' });
      
      const result = await performAction();
      
      logger.info({ 
        message: 'Action completed', 
        result: result.status 
      });
    } catch (error) {
      logger.error({ 
        message: 'Action failed', 
        error: error.message 
      });
    }
  };

  return (
    <TouchableOpacity onPress={handleAction}>
      <Text>Perform Action</Text>
    </TouchableOpacity>
  );
}

Available Methods ​

jsx
// Info level
logger.info({ message: 'User logged in', userId: '123' });

// Warning level
logger.warning({ message: 'Low storage space' });

// Error level
logger.error({ message: 'Network request failed', error: err.message });

// Critical level
logger.critical({ message: 'App crash prevented' });

// Custom level
logger.send('custom', { message: 'Custom event' });

Context Management ​

Set and clear context for multiple logs:

jsx
import { useEffect } from 'react';

function UserProfileScreen({ route }) {
  const logger = logstyx({ 
    projectId: 'proj_abc123', 
    appid: 'com.example.app' 
  });
  
  const { userId } = route.params;

  useEffect(() => {
    // Set context when screen mounts
    logger.setContext({
      userId: userId,
      screen: 'UserProfile',
      platform: Platform.OS
    });

    // Clear context when screen unmounts
    return () => logger.clearContext();
  }, [userId]);

  const handleUpdate = () => {
    // This log will include the context
    logger.info({ message: 'Profile updated' });
  };

  return (
    <TouchableOpacity onPress={handleUpdate}>
      <Text>Update Profile</Text>
    </TouchableOpacity>
  );
}

Complete Examples ​

jsx
import { NavigationContainer } from '@react-navigation/native';
import { useEffect } from 'react';

function App() {
  const logger = logstyx({
    projectId: 'proj_abc123',
    appid: 'com.example.app',
    endpoint: 'https://logs.example.com/api/v1/logs'
  });

  const { ErrorBoundary } = logger;

  const onNavigationStateChange = (state) => {
    const currentRoute = getCurrentRouteName(state);
    
    logger.info({
      message: 'Screen view',
      screen: currentRoute,
      platform: Platform.OS
    });
  };

  return (
    <ErrorBoundary logger={logger}>
      <NavigationContainer onStateChange={onNavigationStateChange}>
        {/* Your navigation */}
      </NavigationContainer>
    </ErrorBoundary>
  );
}

function getCurrentRouteName(state) {
  if (!state) return null;
  const route = state.routes[state.index];
  if (route.state) {
    return getCurrentRouteName(route.state);
  }
  return route.name;
}

E-commerce App Tracking ​

jsx
import { View, Text, TouchableOpacity, Image } from 'react-native';

function ProductScreen({ product }) {
  const logger = logstyx({ 
    projectId: 'proj_abc123', 
    appid: 'com.example.app' 
  });
  
  const { Trackable } = logger;

  return (
    <View>
      <Image source={{ uri: product.image }} />
      <Text>{product.name}</Text>
      <Text>${product.price}</Text>

      <Trackable 
        event="press"
        context={{ screen: 'product', productId: product.id }}
        data={{ action: 'add_to_cart', price: product.price }}>
        <TouchableOpacity style={styles.addButton}>
          <Text>Add to Cart</Text>
        </TouchableOpacity>
      </Trackable>

      <Trackable 
        event="press"
        context={{ screen: 'product', productId: product.id }}
        data={{ action: 'buy_now', price: product.price }}
        level="info">
        <TouchableOpacity style={styles.buyButton}>
          <Text>Buy Now</Text>
        </TouchableOpacity>
      </Trackable>

      <Trackable 
        event="press"
        context={{ screen: 'product', productId: product.id }}
        data={{ action: 'share' }}>
        <TouchableOpacity>
          <Text>Share</Text>
        </TouchableOpacity>
      </Trackable>
    </View>
  );
}

Form Tracking ​

jsx
import { useState } from 'react';
import { View, TextInput, TouchableOpacity, Text } from 'react-native';

function SignupScreen() {
  const logger = logstyx({ 
    projectId: 'proj_abc123', 
    appid: 'com.example.app' 
  });
  
  const { Trackable } = logger;
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleSignup = async () => {
    logger.info({ 
      message: 'Signup attempted',
      platform: Platform.OS 
    });

    try {
      await signup(email, password);
      
      logger.info({ 
        message: 'Signup successful',
        method: 'email' 
      });
    } catch (error) {
      logger.error({ 
        message: 'Signup failed', 
        error: error.message 
      });
    }
  };

  return (
    <View>
      <Trackable 
        event="pressIn pressOut"
        context={{ field: 'email', form: 'signup' }}>
        <TextInput
          value={email}
          onChangeText={setEmail}
          placeholder="Email"
          keyboardType="email-address"
        />
      </Trackable>

      <Trackable 
        event="pressIn pressOut"
        context={{ field: 'password', form: 'signup' }}>
        <TextInput
          value={password}
          onChangeText={setPassword}
          placeholder="Password"
          secureTextEntry
        />
      </Trackable>

      <Trackable 
        event="press"
        data={{ form: 'signup', action: 'submit' }}>
        <TouchableOpacity onPress={handleSignup}>
          <Text>Sign Up</Text>
        </TouchableOpacity>
      </Trackable>
    </View>
  );
}

App Launch Tracking ​

jsx
import { useEffect } from 'react';
import { Platform, Dimensions } from 'react-native';

function App() {
  const logger = logstyx({
    projectId: 'proj_abc123',
    appid: 'com.example.app',
    captureUncaught: true,
    captureUnhandledRejections: true
  });

  useEffect(() => {
    const { width, height } = Dimensions.get('window');
    
    logger.info({
      message: 'App launched',
      platform: Platform.OS,
      version: Platform.Version,
      screen: `${width}x${height}`,
      timestamp: Date.now()
    });
  }, []);

  const { ErrorBoundary } = logger;

  return (
    <ErrorBoundary logger={logger}>
      <YourApp />
    </ErrorBoundary>
  );
}

Best Practices ​

1. Initialize Once at App Root ​

jsx
// ✅ Good: Initialize at app root
function App() {
  const logger = logstyx({ 
    projectId: 'proj_abc123', 
    appid: 'com.example.app' 
  });
  
  const { ErrorBoundary } = logger;

  return (
    <ErrorBoundary logger={logger}>
      <NavigationContainer>
        {/* Your screens */}
      </NavigationContainer>
    </ErrorBoundary>
  );
}

// ❌ Avoid: Re-initializing in every screen
function MyScreen() {
  const logger = logstyx({ /* ... */ }); // Don't do this
}

2. Use Context for Screen-Specific Logs ​

jsx
function CheckoutScreen() {
  const logger = logstyx({ 
    projectId: 'proj_abc123', 
    appid: 'com.example.app' 
  });

  useEffect(() => {
    logger.setContext({ 
      screen: 'checkout',
      platform: Platform.OS 
    });
    
    return () => logger.clearContext();
  }, []);

  // All logs will include the context
  const handlePayment = () => {
    logger.info({ message: 'Payment initiated' });
  };
}

3. Trackable for Simple Interactions ​

jsx
// ✅ Good: Use Trackable for simple taps
<Trackable event="press" data={{ action: 'download' }}>
  <TouchableOpacity>
    <Text>Download</Text>
  </TouchableOpacity>
</Trackable>

// ✅ Good: Use manual logging for complex logic
const handleComplexAction = async () => {
  logger.info({ message: 'Action started' });
  
  try {
    const result = await complexOperation();
    logger.info({ message: 'Action completed', result });
  } catch (error) {
    logger.error({ message: 'Action failed', error: error.message });
  }
};

4. Enable Error Capture in Production ​

jsx
import { __DEV__ } from 'react-native';

const logger = logstyx({
  projectId: process.env.LOGSTYX_PROJECT_ID,
  appid: 'com.example.app',
  endpoint: process.env.LOGSTYX_ENDPOINT,
  captureUncaught: !__DEV__,  // Only in production
  captureUnhandledRejections: !__DEV__
});

5. Track App Lifecycle Events ​

jsx
import { AppState } from 'react-native';

function App() {
  const logger = logstyx({ 
    projectId: 'proj_abc123', 
    appid: 'com.example.app' 
  });

  useEffect(() => {
    const subscription = AppState.addEventListener('change', (nextAppState) => {
      logger.info({
        message: 'App state changed',
        state: nextAppState,
        platform: Platform.OS
      });
    });

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

  return <YourApp />;
}

6. Use Platform-Specific Context ​

jsx
import { Platform } from 'react-native';

useEffect(() => {
  logger.setContext({
    platform: Platform.OS,
    version: Platform.Version,
    ...Platform.select({
      ios: { device: 'iPhone' },
      android: { device: 'Android' }
    })
  });
}, []);

Troubleshooting ​

Trackable Component Not Working ​

Issue: Touch events aren't being tracked

Solution: Ensure Trackable has exactly one child:

jsx
// ❌ Wrong: Multiple children
<Trackable event="press">
  <TouchableOpacity>Button 1</TouchableOpacity>
  <TouchableOpacity>Button 2</TouchableOpacity>
</Trackable>

// ✅ Correct: Single child
<Trackable event="press">
  <View>
    <TouchableOpacity>Button 1</TouchableOpacity>
    <TouchableOpacity>Button 2</TouchableOpacity>
  </View>
</Trackable>

ErrorBoundary Not Catching Errors ​

Issue: Errors aren't being caught

Solution: ErrorBoundary only catches errors in child components. It cannot catch:

  • Errors in event handlers (use try-catch)
  • Asynchronous code (use try-catch)
  • Errors in ErrorBoundary itself
jsx
// ❌ Won't be caught by ErrorBoundary
<TouchableOpacity onPress={() => {
  throw new Error('Press error');
}}>
  <Text>Press</Text>
</TouchableOpacity>

// ✅ Will be logged
<TouchableOpacity onPress={() => {
  try {
    riskyOperation();
  } catch (error) {
    logger.error({ message: 'Error', error: error.message });
  }
}}>
  <Text>Press</Text>
</TouchableOpacity>

Finding Your App ID ​

Android:

gradle
// android/app/build.gradle
android {
    defaultConfig {
        applicationId "com.yourcompany.app" // This is your appid
    }
}

iOS:

// In Xcode:
// Target → General → Bundle Identifier
// e.g., com.yourcompany.app

Network Errors ​

Issue: Logs not reaching server

Solution:

  • Verify endpoint URL is correct
  • Check network permissions in app config
  • Test endpoint accessibility from device/simulator
  • Check for HTTPS requirements on iOS

Platform-Specific Notes ​

iOS ​

  • Ensure App Transport Security (ATS) allows your endpoint
  • Add network permission in Info.plist if needed
  • Test on real devices for accurate error tracking

Android ​

  • Add internet permission in AndroidManifest.xml:
    xml
    <uses-permission android:name="android.permission.INTERNET" />
  • Consider network security config for custom endpoints
  • Test on various Android versions

Released under the MIT License.