npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

riuve-rn

v1.0.2

Published

Behavioral event tracking SDK for React Native with offline support and intelligent batching

Readme

Riuve SDK - React Native/Expo

Behavioral event tracking SDK for React Native and Expo applications


Table of Contents

  1. What is Riuve SDK?
  2. Installation
  3. Quick Start
  4. Advanced Features
  5. Best Practices
  6. Troubleshooting
  7. API Reference

What is Riuve SDK?

Riuve SDK is a behavioral event tracking library for React Native and Expo applications.

Features

  • Offline Support: Events are queued when offline and automatically sent when connection is restored
  • Intelligent Batching: Events are automatically batched for optimal performance
  • Automatic Retry: Failed requests are automatically retried with exponential backoff
  • Background Flush: Events are automatically flushed when app goes to background
  • User Identification: Manages both anonymous and external user IDs
  • Push Notification Support: FCM token management
  • Zero Configuration: Backend URL is hardcoded, only API key is required
  • Pending Events Queue: Events tracked before SDK initialization are automatically processed

Use Cases

  • Track user behavior and interactions in your app
  • Monitor which screens are viewed most
  • Track button clicks, form submissions, and user actions
  • Segment users based on their behavior
  • Send targeted push notifications

Installation

1. Install the Package

npm install riuve-rn

or

yarn add riuve-rn

2. Install Required Dependencies

AsyncStorage is required for the SDK to function:

npm install @react-native-async-storage/async-storage

3. (Optional) Expo Modules

If you're using Expo, install these for device info and network monitoring:

npx expo install expo-device expo-application expo-localization expo-network

4. (Optional) Native Modules

For better performance with native modules (Development Build or Bare React Native):

npm install react-native-device-info @react-native-community/netinfo

Quick Start

Step 1: Initialize the SDK

Initialize the SDK at the start of your app (typically in App.tsx or _layout.tsx):

import { useEffect } from 'react';
import Riuve from 'riuve-rn';

export default function App() {
  useEffect(() => {
    initializeSDK();
  }, []);

  const initializeSDK = async () => {
    try {
      // Get your API key from Riuve dashboard
      await Riuve.initialize('riuve_base_xxxxxxxxxxxxxxxxxxxxx', {
        // Basic configuration
        batchSize: 10,        // Number of events per batch (default: 10)
        batchTimeout: 30000,  // Auto-send after 30 seconds (default: 30000ms)
      });

      console.log('✅ SDK initialized successfully');
    } catch (error) {
      console.error('❌ SDK initialization error:', error);
    }
  };

  return (
    // Your app content
  );
}

Important Notes:

  • The baseUrl is hardcoded in the SDK (https://api.riuve.com), no need to pass it
  • Get your API key from the Riuve Dashboard

Step 2: Identify Users

When a user logs in or user information is available:

import Riuve from 'riuve-rn';

// When user logs in
const handleLogin = async (userId: string, userEmail: string) => {
  try {
    await Riuve.identify(userId, {
      email: userEmail,
      name: 'User Name',
      user_type: 'premium',
      // Add any custom properties you want
    });

    console.log('✅ User identified');
  } catch (error) {
    console.error('❌ Identify error:', error);
  }
};

When to Identify:

  • When user logs in
  • When user information is updated
  • On app start (if user is already logged in)

Step 3: Track Events

Track important actions in your app:

import Riuve from 'riuve-rn';
import { useFocusEffect } from '@react-navigation/native';
import { useCallback } from 'react';

// Screen view tracking (use useFocusEffect for tabs)
useFocusEffect(
  useCallback(() => {
    Riuve.track('screen_viewed', {
      screen_name: 'Home',
      screen_category: 'main',
    }).catch(console.error);
  }, [])
);

// Button click
const handleButtonClick = async () => {
  try {
    await Riuve.track('button_clicked', {
      button_id: 'subscribe_button',
      button_text: 'Subscribe',
      screen: 'Home',
    });
    
    // Button action
    // ...
  } catch (error) {
    console.error('Track error:', error);
  }
};

// Form submission
const handleFormSubmit = async (formData: any) => {
  try {
    await Riuve.track('form_submitted', {
      form_name: 'contact_form',
      form_fields: Object.keys(formData).length,
      success: true,
    });
  } catch (error) {
    console.error('Track error:', error);
  }
};

Event Naming Best Practices:

  • Use snake_case: screen_viewed, button_clicked
  • Use descriptive names: user_subscribed ✅, event1
  • Be consistent: screen_viewed for all screens, button_clicked for all buttons

Advanced Features

1. Custom Event Properties

Add detailed information to events:

await Riuve.track('purchase_completed', {
  product_id: 'prod_123',
  product_name: 'Premium Subscription',
  price: 99.99,
  currency: 'USD',
  payment_method: 'credit_card',
  discount_code: 'SUMMER2024',
});

2. Manual Flush

Send events immediately when needed:

await Riuve.flush();

When to Use:

  • Before user exits the app for important events
  • After critical actions (e.g., payment completed)

3. Reset User

When user logs out:

await Riuve.reset();

This will:

  • Clear external user ID
  • Clear user properties
  • Generate a new anonymous ID
  • Clear event queue

4. FCM Token Management

Register FCM token for push notifications:

import * as Notifications from 'expo-notifications';

// Get FCM token
const token = await Notifications.getExpoPushTokenAsync();

// Register with SDK
await Riuve.setFcmToken(token.data);

Best Practices

1. Initialize Early

Initialize the SDK as early as possible:

// ✅ GOOD: At app start
export default function App() {
  useEffect(() => {
    Riuve.initialize('api_key');
  }, []);
}

// ❌ BAD: After user action
const handleButtonClick = () => {
  Riuve.initialize('api_key'); // Too late!
};

2. Error Handling

Always use try-catch:

// ✅ GOOD
try {
  await Riuve.track('event_name', {});
} catch (error) {
  console.error('Track failed:', error);
  // Don't break app flow, just log it
}

// ❌ BAD
await Riuve.track('event_name', {}); // App may crash on error

3. Async/Await vs Promise

// ✅ GOOD: Async/await (more readable)
const handleAction = async () => {
  try {
    await Riuve.track('action_completed');
  } catch (error) {
    console.error(error);
  }
};

// ✅ GOOD: Promise catch (for quick actions)
Riuve.track('quick_action').catch(console.error);

// ❌ BAD: Unhandled promise
Riuve.track('action'); // Fails silently on error

4. Event Naming Convention

Use a consistent naming convention:

// ✅ GOOD: Consistent and descriptive
Riuve.track('screen_viewed', { screen_name: 'Home' });
Riuve.track('screen_viewed', { screen_name: 'Profile' });
Riuve.track('button_clicked', { button_id: 'subscribe' });
Riuve.track('button_clicked', { button_id: 'cancel' });

// ❌ BAD: Inconsistent
Riuve.track('home_viewed');
Riuve.track('profile_screen');
Riuve.track('click_subscribe');
Riuve.track('cancelButton');

5. Tab Navigation Events

Use useFocusEffect for tab screens to avoid tracking events for unmounted tabs:

import { useFocusEffect } from '@react-navigation/native';

// ✅ GOOD: Only tracks when tab is actually focused
useFocusEffect(
  useCallback(() => {
    Riuve.track('screen_viewed', {
      screen_name: 'Home',
    }).catch(console.error);
  }, [])
);

// ❌ BAD: Tracks even if tab is not visible
useEffect(() => {
  Riuve.track('screen_viewed', {
    screen_name: 'Home',
  }).catch(console.error);
}, []);

6. Critical Events

Use flush for critical events:

// Payment completed - send immediately
await Riuve.track('purchase_completed', {
  order_id: 'order_123',
  amount: 99.99,
});
await Riuve.flush(); // Send immediately

// Screen view - send in batch (normal)
Riuve.track('screen_viewed', { screen_name: 'Home' });

Configuration Options

All Configuration Parameters

await Riuve.initialize('api_key', {
  // Debugging
  debugMode: false,           // Enable detailed logging (default: false)

  // Batch Settings
  batchSize: 10,              // Number of events per batch (default: 10)
  batchTimeout: 30000,        // Auto-send timeout in ms (default: 30000)

  // Network Settings
  requestTimeout: 10000,      // API request timeout in ms (default: 10000)

  // Retry Settings
  maxRetries: 3,              // Maximum retry attempts (default: 3)
  retryDelay: 1000,           // Retry delay in ms (default: 1000)

  // Storage Settings
  persistEvents: true,        // Save events to storage (default: true)
  maxQueueSize: 1000,         // Maximum queue size (default: 1000)
  eventRetentionDays: 7,      // Days to keep failed events (default: 7)

  // Automatic Operations
  autoFlushOnBackground: true, // Auto-flush on background (default: true)
});

Recommended Configurations

Development:

{
  debugMode: true,     // Enable detailed logging
  batchSize: 5,        // Smaller batches (for testing)
  batchTimeout: 10000, // Faster sending (for testing)
}

Production:

{
  debugMode: false,     // Only WARN and ERROR logs (default)
  batchSize: 10,        // Standard batch size
  batchTimeout: 30000,  // Wait 30 seconds
  autoFlushOnBackground: true, // Send when backgrounded
}

High Volume (Many events):

{
  batchSize: 20,        // Larger batches
  batchTimeout: 60000,  // Wait 1 minute
  maxQueueSize: 5000,   // Larger queue
}

Troubleshooting

Problem 1: SDK Not Initializing

Symptoms:

  • SDK not initialized error
  • Events are not being tracked

Solution:

// Wait for initialization to complete
useEffect(() => {
  const init = async () => {
    await Riuve.initialize('api_key');
    console.log('SDK initialized');
  };
  init();
}, []);

// Check before tracking
if (Riuve.isReady()) {
  await Riuve.track('event');
}

Note: Events tracked before initialization are automatically queued and processed after initialization.

Problem 2: Events Not Sending

Checklist:

  1. ✅ Is SDK initialized?
  2. ✅ Is API key correct?
  3. ✅ Is there internet connection?

Debug:

// Enable debug mode to see detailed logs
await Riuve.initialize('api_key', {
  debugMode: true, // Shows DEBUG, INFO, WARN, and ERROR logs
});

// With debugMode: false (default), only WARN and ERROR are visible:
// [Riuve SDK] [WARN] Network offline, events will be stored
// [Riuve SDK] [ERROR] Failed to send batch

// With debugMode: true, you'll see everything:
// [Riuve SDK] [DEBUG] Config: {...}
// [Riuve SDK] [INFO] Making HTTP request: POST /api/sdk/track
// [Riuve SDK] [INFO] Event tracked: button_clicked
// [Riuve SDK] [WARN] Retry attempt 1...
// [Riuve SDK] [ERROR] Failed after 3 retries

Problem 3: Offline Events Not Sending

Normal Behavior:

  • Offline events are saved to AsyncStorage
  • Automatically sent when internet connection is restored
  • Sent when app is reopened

Manual Check:

// Manually flush
await Riuve.flush();

// Or on app start
useEffect(() => {
  Riuve.initialize('api_key').then(() => {
    Riuve.flush(); // Send pending events
  });
}, []);

Problem 4: Too Many Events Accumulating

Solution:

// Increase batch size
await Riuve.initialize('api_key', {
  batchSize: 20,        // Larger batches
  batchTimeout: 15000,  // Send more frequently
});

Problem 5: TypeScript Errors

Solution:

// Use type assertion
await Riuve.track('event_name', {
  custom_prop: value,
} as any); // Temporary solution

// Or proper typing
interface MyEventProperties {
  custom_prop: string;
}

await Riuve.track('event_name', {
  custom_prop: 'value',
} as MyEventProperties);

API Reference

Riuve.initialize()

Initializes the SDK.

await Riuve.initialize(apiKey: string, config?: RiuveConfig): Promise<void>

Parameters:

  • apiKey (string, required): Your API key from Riuve dashboard
  • config (object, optional): Configuration object

Example:

await Riuve.initialize('riuve_base_xxx', {
  batchSize: 10,
});

Riuve.identify()

Identifies a user.

await Riuve.identify(externalUserId: string, properties?: object): Promise<void>

Parameters:

  • externalUserId (string, required): User's unique ID
  • properties (object, optional): User properties

Example:

await Riuve.identify('user_123', {
  email: '[email protected]',
  name: 'John Doe',
});

Note: If user properties are updated, call identify again with the new properties.

Riuve.track()

Tracks an event.

await Riuve.track(eventName: string, properties?: object): Promise<void>

Parameters:

  • eventName (string, required): Event name
  • properties (object, optional): Event properties

Example:

await Riuve.track('button_clicked', {
  button_id: 'subscribe',
  screen: 'Home',
});

Note: Can be called before SDK initialization. Events will be queued and processed after initialization.

Riuve.flush()

Immediately sends all pending events.

await Riuve.flush(): Promise<void>

Example:

await Riuve.track('purchase_completed');
await Riuve.flush(); // Send immediately

Riuve.reset()

Clears user information and queue.

await Riuve.reset(): Promise<void>

Example:

// When user logs out
await Riuve.reset();

Riuve.setFcmToken()

Registers FCM token (for push notifications).

await Riuve.setFcmToken(token: string): Promise<void>

Example:

const token = await Notifications.getExpoPushTokenAsync();
await Riuve.setFcmToken(token.data);

Riuve.isReady()

Checks if SDK is initialized.

const isReady: boolean = Riuve.isReady();

Example:

if (Riuve.isReady()) {
  await Riuve.track('event');
}

Riuve.getQueueSize()

Gets current queue size.

const queueSize: number = Riuve.getQueueSize();

Riuve.getFailedBatchCount()

Gets number of failed batches waiting for retry.

const failedCount: number = await Riuve.getFailedBatchCount();

Example Scenarios

Scenario 1: E-Commerce App

// 1. On app start
useEffect(() => {
  Riuve.initialize('api_key');
}, []);

// 2. When user logs in
const handleLogin = async (user: User) => {
  await Riuve.identify(user.id, {
    email: user.email,
    name: user.name,
    membership_type: user.membershipType,
  });
};

// 3. Product view
const handleProductView = (product: Product) => {
  Riuve.track('product_viewed', {
    product_id: product.id,
    product_name: product.name,
    product_category: product.category,
    price: product.price,
  });
};

// 4. Add to cart
const handleAddToCart = (product: Product) => {
  Riuve.track('add_to_cart', {
    product_id: product.id,
    product_name: product.name,
    price: product.price,
    quantity: 1,
  });
};

// 5. Purchase completed
const handlePurchaseComplete = async (order: Order) => {
  await Riuve.track('purchase_completed', {
    order_id: order.id,
    total_amount: order.total,
    items_count: order.items.length,
    payment_method: order.paymentMethod,
  });
  await Riuve.flush(); // Critical event, send immediately
};

Scenario 2: Social Media App

// 1. Post view
const handlePostView = (post: Post) => {
  Riuve.track('post_viewed', {
    post_id: post.id,
    author_id: post.authorId,
    post_type: post.type,
  });
};

// 2. Like/Unlike
const handleLike = (post: Post) => {
  Riuve.track('post_liked', {
    post_id: post.id,
    author_id: post.authorId,
  });
};

// 3. Comment
const handleComment = (post: Post, comment: string) => {
  Riuve.track('comment_added', {
    post_id: post.id,
    comment_length: comment.length,
  });
};

// 4. Profile view
const handleProfileView = (userId: string) => {
  Riuve.track('profile_viewed', {
    profile_user_id: userId,
  });
};

Scenario 3: Game App

// 1. Level start
const handleLevelStart = (level: number) => {
  Riuve.track('level_started', {
    level_number: level,
    difficulty: 'normal',
  });
};

// 2. Level completion
const handleLevelComplete = async (level: number, score: number) => {
  await Riuve.track('level_completed', {
    level_number: level,
    score: score,
    time_spent: getTimeSpent(),
  });
  await Riuve.flush(); // Important event
};

// 3. In-app purchase
const handleInAppPurchase = async (item: Item) => {
  await Riuve.track('in_app_purchase', {
    item_id: item.id,
    item_name: item.name,
    price: item.price,
    currency: 'USD',
  });
  await Riuve.flush();
};

Security

API Key Security

  • Never commit API keys to public repositories
  • ✅ Use environment variables or secure storage
  • ✅ Use different API keys for different projects

Example:

// ✅ GOOD: Environment variable
const API_KEY = process.env.EXPO_PUBLIC_RIUVE_API_KEY || '';

// ❌ BAD: Hardcoded
const API_KEY = 'riuve_base_xxxxxxxxxxxxx';

Support

For questions and support:


License

MIT


Last Updated: 2025-01-29 SDK Version: 1.1.1