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

rive-react-native-sdk

v1.1.3

Published

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

Downloads

1,066

Readme

Rive SDK - React Native/Expo

Behavioral event tracking SDK for React Native and Expo applications


Table of Contents

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

What is Rive SDK?

Rive 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 rive-react-native-sdk

or

yarn add rive-react-native-sdk

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 Rive from 'rive-react-native-sdk';

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

  const initializeSDK = async () => {
    try {
      // Get your API key from Nock dashboard
      await Rive.initialize('nock_live_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.nockspace.com), no need to pass it
  • Get your API key from the Nock Dashboard

Step 2: Identify Users

When a user logs in or user information is available:

import Rive from 'rive-react-native-sdk';

// When user logs in
const handleLogin = async (userId: string, userEmail: string) => {
  try {
    await Rive.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 Rive from 'rive-react-native-sdk';
import { useFocusEffect } from '@react-navigation/native';
import { useCallback } from 'react';

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

// Button click
const handleButtonClick = async () => {
  try {
    await Rive.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 Rive.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 Rive.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 Rive.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 Rive.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 Rive.setFcmToken(token.data);

Best Practices

1. Initialize Early

Initialize the SDK as early as possible:

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

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

2. Error Handling

Always use try-catch:

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

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

3. Async/Await vs Promise

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

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

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

4. Event Naming Convention

Use a consistent naming convention:

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

// ❌ BAD: Inconsistent
Rive.track('home_viewed');
Rive.track('profile_screen');
Rive.track('click_subscribe');
Rive.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(() => {
    Rive.track('screen_viewed', {
      screen_name: 'Home',
    }).catch(console.error);
  }, [])
);

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

6. Critical Events

Use flush for critical events:

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

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

Configuration Options

All Configuration Parameters

await Rive.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 Rive.initialize('api_key');
    console.log('SDK initialized');
  };
  init();
}, []);

// Check before tracking
if (Rive.isReady()) {
  await Rive.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 Rive.initialize('api_key', {
  debugMode: true, // Shows DEBUG, INFO, WARN, and ERROR logs
});

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

// With debugMode: true, you'll see everything:
// [Rive SDK] [DEBUG] Config: {...}
// [Rive SDK] [INFO] Making HTTP request: POST /api/sdk/track
// [Rive SDK] [INFO] Event tracked: button_clicked
// [Rive SDK] [WARN] Retry attempt 1...
// [Rive 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 Rive.flush();

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

Problem 4: Too Many Events Accumulating

Solution:

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

Problem 5: TypeScript Errors

Solution:

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

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

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

API Reference

Rive.initialize()

Initializes the SDK.

await Rive.initialize(apiKey: string, config?: RiveConfig): Promise<void>

Parameters:

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

Example:

await Rive.initialize('nock_live_xxx', {
  batchSize: 10,
});

Rive.identify()

Identifies a user.

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

Parameters:

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

Example:

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

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

Rive.track()

Tracks an event.

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

Parameters:

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

Example:

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

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

Rive.flush()

Immediately sends all pending events.

await Rive.flush(): Promise<void>

Example:

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

Rive.reset()

Clears user information and queue.

await Rive.reset(): Promise<void>

Example:

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

Rive.setFcmToken()

Registers FCM token (for push notifications).

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

Example:

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

Rive.isReady()

Checks if SDK is initialized.

const isReady: boolean = Rive.isReady();

Example:

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

Rive.getQueueSize()

Gets current queue size.

const queueSize: number = Rive.getQueueSize();

Rive.getFailedBatchCount()

Gets number of failed batches waiting for retry.

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

Example Scenarios

Scenario 1: E-Commerce App

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

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

// 3. Product view
const handleProductView = (product: Product) => {
  Rive.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) => {
  Rive.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 Rive.track('purchase_completed', {
    order_id: order.id,
    total_amount: order.total,
    items_count: order.items.length,
    payment_method: order.paymentMethod,
  });
  await Rive.flush(); // Critical event, send immediately
};

Scenario 2: Social Media App

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

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

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

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

Scenario 3: Game App

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

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

// 3. In-app purchase
const handleInAppPurchase = async (item: Item) => {
  await Rive.track('in_app_purchase', {
    item_id: item.id,
    item_name: item.name,
    price: item.price,
    currency: 'USD',
  });
  await Rive.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_RIVE_API_KEY || '';

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

Support

For questions and support:


License

MIT


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