
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@layers/react-native
Advanced tools
Layers Analytics React Native SDK — thin wrapper over Rust core via WASM
@layers/react-native is the Layers analytics SDK for React Native apps. It provides event tracking, screen tracking, user identification, App Tracking Transparency (ATT), SKAdNetwork (SKAN), deep link handling, clipboard-based deferred deep links, consent management, and automatic lifecycle and connectivity handling.
Use this package for bare React Native projects. For Expo managed workflow projects, use @layers/expo instead.
npm install @layers/react-native
# or
yarn add @layers/react-native
npm install @react-native-async-storage/async-storage @react-native-community/netinfo
These are optional but strongly recommended:
cd ios && pod install
Add NSUserTrackingUsageDescription to your Info.plist if you plan to use ATT.
import { LayersReactNative } from '@layers/react-native';
// Create and initialize
const layers = new LayersReactNative({
appId: 'your-app-id',
environment: 'production'
});
await layers.init();
// Track events
layers.track('button_tapped', { button_name: 'signup' });
// Track screen views
layers.screen('Home');
// Identify users
layers.setAppUserId('user_123');
interface LayersRNConfig {
appId: string;
environment: 'development' | 'staging' | 'production';
appUserId?: string;
enableDebug?: boolean; // default: false
baseUrl?: string; // default: "https://in.layers.com"
flushIntervalMs?: number; // default: 30000
flushThreshold?: number; // default: 10
maxQueueSize?: number; // default: 1000
autoTrackAppOpen?: boolean; // default: true
autoTrackDeepLinks?: boolean; // default: true
}
| Option | Type | Default | Description |
|---|---|---|---|
appId | string | required | Your Layers application identifier. |
environment | Environment | required | 'development', 'staging', or 'production'. |
appUserId | string | undefined | Optional user ID to set at construction time. |
enableDebug | boolean | false | Enable verbose console logging. |
baseUrl | string | "https://in.layers.com" | Custom ingest API endpoint. |
flushIntervalMs | number | 30000 | Automatic flush interval in milliseconds. |
flushThreshold | number | 10 | Queue size that triggers an automatic flush. |
maxQueueSize | number | 1000 | Maximum events in the queue before dropping. |
autoTrackAppOpen | boolean | true | Automatically track app_open on init. |
autoTrackDeepLinks | boolean | true | Automatically track deep_link_opened events. |
const layers = new LayersReactNative(config: LayersRNConfig);
await layers.init();
The constructor creates the SDK instance with an in-memory queue. Calling init() upgrades to AsyncStorage-backed persistence, collects device info, fetches remote config, reads clipboard attribution (iOS), fires app_open, and sets up auto-tracking.
You can call track() and screen() before init() completes -- events are queued in memory.
track(eventName: string, properties?: EventProperties): void
layers.track('purchase_completed', {
product_id: 'sku_123',
price: 9.99,
currency: 'USD'
});
screen(screenName: string, properties?: EventProperties): void
layers.screen('ProductDetail', { product_id: 'sku_123' });
// Set the app user ID (set-user-once: ignored if already set)
setAppUserId(appUserId: string): void
// Clear the current user ID (allows setting a new one)
clearAppUserId(): void
// Get the current user ID
getAppUserId(): string | undefined
// Set user properties
async setUserProperties(properties: UserProperties): Promise<void>
// After login
layers.setAppUserId('user_123');
await layers.setUserProperties({
email: 'user@example.com',
plan: 'premium'
});
// On logout
layers.clearAppUserId();
Set-user-once semantics: Once
setAppUserId()is called, subsequent calls are ignored untilclearAppUserId()is called. This prevents accidental user ID changes.
async setConsent(consent: ConsentState): Promise<void>
getConsentState(): ConsentState
interface ConsentState {
analytics?: boolean;
advertising?: boolean;
}
// User accepts all tracking
await layers.setConsent({ analytics: true, advertising: true });
// User denies advertising
await layers.setConsent({ analytics: true, advertising: false });
// Read current consent
const consent = layers.getConsentState();
// Flush queued events to the server
async flush(): Promise<void>
// Shut down the SDK (removes listeners, stops timers)
shutdown(): void
// Get the current session ID
getSessionId(): string
// Override device context fields
setDeviceInfo(deviceInfo: DeviceContext): void
// Register an error listener
on(event: 'error', listener: (error: Error) => void): this
// Remove an error listener
off(event: 'error', listener: (error: Error) => void): this
layers.on('error', (error) => {
console.error('Layers error:', error.message);
// Forward to your crash reporting service
});
The requestTrackingPermission() method on the SDK instance handles the full ATT flow:
const status = await layers.requestTrackingPermission();
// Returns: 'authorized' | 'denied' | 'restricted' | 'not_determined'
This method automatically:
expo-tracking-transparency if available)For more granular control, use the exported functions:
import {
getATTStatus,
getAdvertisingId,
getVendorId,
isATTAvailable,
requestTrackingAuthorization
} from '@layers/react-native';
const status = await getATTStatus();
const isAvailable = await isATTAvailable();
const idfa = await getAdvertisingId(); // null if not authorized
const idfv = await getVendorId(); // always available on iOS
type ATTStatus = 'not_determined' | 'restricted' | 'denied' | 'authorized';
Important: Add
NSUserTrackingUsageDescriptionto yourInfo.plist.
SKAN is auto-configured from the server's remote config. The SDK creates a SKANManager instance and automatically forwards every track() call through the SKAN rule engine.
const skanManager = layers.getSkanManager();
if (skanManager) {
const metrics = skanManager.getMetrics();
console.log('SKAN value:', metrics.currentValue);
console.log('SKAN preset:', metrics.currentPreset);
}
If you need to configure SKAN manually instead of relying on remote config:
import { SKANManager } from '@layers/react-native';
const skan = new SKANManager((data) => {
console.log(`Conversion value updated: ${data.previousValue} -> ${data.newValue}`);
});
// Use a built-in preset
skan.setPreset('subscriptions'); // or 'engagement' or 'iap'
await skan.initialize();
// Or define custom rules
skan.setCustomRules([
{
eventName: 'purchase_success',
conditions: { revenue: { '>=': 10 } },
conversionValue: 63,
coarseValue: 'high',
priority: 10
},
{
eventName: 'trial_start',
conversionValue: 20,
priority: 5
}
]);
await skan.initialize();
// Process events manually
await skan.processEvent('purchase_success', { revenue: 49.99 });
interface SKANConversionRule {
eventName: string;
conditions?: Record<string, unknown>; // Operator-based: { '>': 10, '<': 100 }
conversionValue: number; // 0-63 (SKAN 3.0) or 0-7 (SKAN 4.0)
coarseValue?: 'low' | 'medium' | 'high'; // SKAN 4.0 only
lockWindow?: boolean; // SKAN 4.0 only
priority?: number;
description?: string;
}
interface SKANMetrics {
isSupported: boolean;
version: string;
currentValue: number | null;
currentPreset: string | null;
ruleCount: number;
evaluationCount: number;
}
subscriptions -- Optimized for subscription apps (trial start, subscription start/renew)engagement -- Optimized for engagement-driven apps (content views, sessions, bookmarks)iap -- Optimized for in-app purchase revenue tracking (purchase tiers by revenue)When autoTrackDeepLinks is true (default), the SDK automatically tracks a deep_link_opened event for every incoming deep link. The event includes the full URL, scheme, host, path, and all query parameters (UTM, click IDs) as flat top-level properties.
import { parseDeepLink, setupDeepLinkListener } from '@layers/react-native';
const unsubscribe = setupDeepLinkListener((data) => {
console.log('Deep link:', data.url);
console.log('Host:', data.host);
console.log('Path:', data.path);
console.log('UTM Source:', data.queryParams.utm_source);
console.log('Click ID:', data.queryParams.fbclid);
});
// Later: unsubscribe()
The listener handles both:
Linking.getInitialURL() on setup.Linking url events.function parseDeepLink(url: string): DeepLinkData | null;
interface DeepLinkData {
url: string;
scheme: string;
host: string;
path: string;
queryParams: Record<string, string>;
timestamp: number;
}
On iOS, the SDK reads the clipboard on first launch (during init()) for a Layers click URL. If found, the click URL and click ID are included as properties on the app_open event. This is controlled by the server's remote config (clipboard_attribution_enabled).
For manual reading:
import { readClipboardAttribution } from '@layers/react-native';
const data = await readClipboardAttribution();
if (data) {
console.log('Click URL:', data.clickUrl);
console.log('Click ID:', data.clickId);
}
interface ClipboardAttribution {
clickUrl: string;
clickId: string;
}
Requires @react-native-clipboard/clipboard as a peer dependency.
For automatic screen tracking with Expo Router:
import { useLayersExpoRouterTracking } from '@layers/react-native';
import { usePathname, useGlobalSearchParams } from 'expo-router';
function RootLayout() {
useLayersExpoRouterTracking(layers, usePathname, useGlobalSearchParams);
return <Stack />;
}
This hook automatically tracks a screen event every time the Expo Router pathname changes, including route parameters as event properties.
The SDK generates and persists a unique install ID via AsyncStorage:
import { getOrSetInstallId } from '@layers/react-native';
const installId = await getOrSetInstallId();
This ID persists across app sessions and is included in the device context.
init() with clipboard attribution (iOS).@react-native-community/netinfo).All types are exported from the package:
import type {
ATTStatus,
ClipboardAttribution,
ConsentState,
DeepLinkData,
DeviceContext,
Environment,
ErrorListener,
EventProperties,
LayersRNConfig,
SKANConversionRule,
SKANMetrics,
SKANPresetConfig,
UserProperties
} from '@layers/react-native';
FAQs
Layers Analytics React Native SDK — thin wrapper over Rust core via WASM
We found that @layers/react-native demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 4 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.