
Security News
Feross on the 10 Minutes or Less Podcast: Nobody Reads the Code
Socket CEO Feross Aboukhadijeh joins 10 Minutes or Less, a podcast by Ali Rohde, to discuss the recent surge in open source supply chain attacks.
@layers/react-native
Advanced tools
Layers Analytics React Native SDK — thin wrapper over Rust core via WASM
React Native SDK for Layers Analytics with ATT, SKAN, deep link support, and offline queueing.
npm install @layers/react-native
# or
pnpm add @layers/react-native
| Package | Required | What degrades without it |
|---|---|---|
react | Yes | -- |
react-native | Yes | -- |
@react-native-async-storage/async-storage | Optional | Events are only persisted in memory; lost on app kill |
@react-native-community/netinfo | Optional | SDK assumes always-online; no offline queueing |
npm install @react-native-async-storage/async-storage @react-native-community/netinfo
import { LayersReactNative } from '@layers/react-native';
const layers = new LayersReactNative({
apiKey: 'your-api-key',
appId: 'your-app-id',
environment: 'production',
enableDebug: __DEV__
});
await layers.init();
// Track events
await layers.track('app_open', { source: 'push_notification' });
// Track screens
await layers.screen('home');
// Identify a user
layers.setAppUserId('user-123');
All fields for LayersRNConfig:
| Field | Type | Default | Description |
|---|---|---|---|
apiKey | string | required | API key from the Layers dashboard |
appId | string | required | Application identifier |
environment | 'development' | 'staging' | 'production' | required | Deployment environment |
appUserId | string | undefined | Pre-set user ID at init time |
enableDebug | boolean | false | Verbose console logging |
baseUrl | string | https://in.layers.com | Override the ingest endpoint |
flushIntervalMs | number | 30000 | Periodic flush interval (ms) |
flushThreshold | number | 20 | Queue depth that triggers auto-flush |
maxQueueSize | number | 10000 | Max events in queue before dropping |
The SDK uses set-user-once semantics. Once setAppUserId() is called, subsequent calls are ignored until clearAppUserId() is called:
layers.setAppUserId('user-123'); // sets the user ID
layers.setAppUserId('user-456'); // ignored (already set)
layers.clearAppUserId(); // clears it
layers.setAppUserId('user-456'); // now sets to user-456
iOS 14.5+ requires asking for tracking permission before accessing IDFA.
import {
getATTStatus,
getAdvertisingId,
getVendorId,
isATTAvailable,
requestTrackingAuthorization
} from '@layers/react-native';
// Check if ATT is available on this device
const available = await isATTAvailable();
// Request permission (shows the system dialog)
const status = await requestTrackingAuthorization();
// Returns: 'authorized' | 'denied' | 'restricted' | 'not_determined'
// Check current status without prompting
const currentStatus = await getATTStatus();
// Get IDFA (only returns a value when status is 'authorized')
const idfa = await getAdvertisingId();
// Get IDFV (always available, does not require ATT)
const idfv = await getVendorId();
// Wire ATT status into consent
if (status === 'authorized') {
await layers.setConsent({ analytics: true, advertising: true });
} else {
await layers.setConsent({ analytics: true, advertising: false });
}
ATT functions require the native LayersATT module. On Android or when the native module is not linked, they return safe defaults ('not_determined', null, false).
Manage iOS SKAdNetwork conversion values with a rule-based engine:
import { SKANManager } from '@layers/react-native';
const skan = new SKANManager((update) => {
console.log(`Conversion: ${update.previousValue} -> ${update.newValue} (${update.event})`);
});
await skan.initialize();
// Use a built-in preset
skan.setPreset('subscriptions'); // or 'engagement' or 'iap'
// Or define custom rules
skan.setCustomRules([
{ eventName: 'app_open', conversionValue: 1, priority: 1 },
{ eventName: 'trial_start', conversionValue: 20, priority: 5 },
{ eventName: 'subscription_start', conversionValue: 50, priority: 10 }
]);
// Call processEvent for each analytics event to evaluate rules
await skan.processEvent('trial_start', { plan: 'premium' });
// Check current state
console.log(skan.getCurrentValue()); // 20
console.log(skan.getMetrics());
SKAN requires the native LayersSKAN module. On Android or without the module, isSupported() returns false and all native operations are no-ops.
import { parseDeepLink, setupDeepLinkListener } from '@layers/react-native';
const unsubscribe = setupDeepLinkListener((data) => {
console.log(data.url); // 'myapp://product/123?ref=email'
console.log(data.scheme); // 'myapp'
console.log(data.host); // 'product'
console.log(data.path); // '/123'
console.log(data.queryParams); // { ref: 'email' }
// Track the deep link as an event
layers.track('deep_link', {
url: data.url,
source: data.queryParams.ref
});
});
// Clean up
unsubscribe();
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 fires a screen() call on every route change with the pathname and search params as properties.
// Allow analytics, deny advertising
await layers.setConsent({ analytics: true, advertising: false });
// Check current state
const consent = layers.getConsentState();
When analytics is false, track() and screen() calls are silently dropped.
Register error listeners to catch errors that would otherwise be silently dropped:
layers.on('error', (err) => {
console.error('Layers SDK error:', err.message);
});
// Remove a listener
layers.off('error', myListener);
When enableDebug is true and no error listeners are registered, errors are logged to console.warn.
Enable enableDebug: true (or __DEV__) for detailed logging:
[Layers] track("app_open", 1 properties)
[Layers] screen("home", 0 properties)
[Layers] setAppUserId("user-123")
Mock the SDK in tests by replacing the instance with a simple spy object:
const mockLayers = {
init: jest.fn().mockResolvedValue(undefined),
track: jest.fn().mockResolvedValue(undefined),
screen: jest.fn().mockResolvedValue(undefined),
setAppUserId: jest.fn(),
clearAppUserId: jest.fn(),
getAppUserId: jest.fn(),
setConsent: jest.fn().mockResolvedValue(undefined),
getConsentState: jest.fn().mockReturnValue({}),
setUserProperties: jest.fn().mockResolvedValue(undefined),
setDeviceInfo: jest.fn(),
flush: jest.fn().mockResolvedValue(undefined),
shutdown: jest.fn(),
getSessionId: jest.fn().mockReturnValue('mock-session-id'),
on: jest.fn().mockReturnThis(),
off: jest.fn().mockReturnThis()
};
MIT
FAQs
Layers Analytics React Native SDK — thin wrapper over Rust core via WASM
The npm package @layers/react-native receives a total of 40 weekly downloads. As such, @layers/react-native popularity was classified as not popular.
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
Socket CEO Feross Aboukhadijeh joins 10 Minutes or Less, a podcast by Ali Rohde, to discuss the recent surge in open source supply chain attacks.

Research
/Security News
Campaign of 108 extensions harvests identities, steals sessions, and adds backdoors to browsers, all tied to the same C2 infrastructure.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.