Skip to main content

React Native SDK

Segment membership​

This package only evaluates published targeting lists. It has no membership write API and must not hold a Backend application key.

Update members from your backend. Call membership from your backend SDK, not from the React Native app. Flow.

Frontend telemetry​

@ops-ai/react-native-toggly 1.4.0 and @ops-ai/react-native-toggly-core 1.8.0 add one compact reporter to the Core owner. Configure it through provider props or direct Core configuration. @ops-ai/react-native-toggly-core 1.9.0 adds enableVariants and getVariant / getVariantValue; see Hooks → useVariant.

import { useState } from 'react'
import { Pressable, Text, View } from 'react-native'
import { TogglyProvider, useToggly } from '@ops-ai/react-native-toggly'

export default function App() {
const [pricingOpen, setPricingOpen] = useState(false)

return (
<TogglyProvider
appKey="your-app-key"
environment="Production"
enableTelemetry={true}
metricsBaseUrl="https://metrics.toggly.io"
telemetryFlushIntervalMs={45_000}
>
<TelemetryActions onOpenPricing={() => setPricingOpen(true)} />
{pricingOpen && <Text>Pricing</Text>}
</TogglyProvider>
)
}

function TelemetryActions({ onOpenPricing }: { onOpenPricing: () => void }) {
const {
recordUsage, recordView, incrementCounter, setGauge, flushTelemetry,
} = useToggly()

const handleCheckout = async () => {
recordUsage('checkout')
incrementCounter('orders')
setGauge('cartValue', 29.95)
await flushTelemetry()
}

const handleOpenPricing = async () => {
onOpenPricing()
recordView('pricing', 'control')
await flushTelemetry()
}

return (
<View>
<Pressable onPress={handleCheckout}><Text>Checkout</Text></Pressable>
<Pressable onPress={handleOpenPricing}><Text>Open pricing</Text></Pressable>
</View>
)
}

The provider forwards background and inactive AppState transitions to its Core reporter and disposes that owner on final unmount or configuration replacement. Direct TogglyService owners must call synchronous dispose() themselves. AsyncStorage and MMKV companions persist feature data only; they do not own or persist telemetry. Record explicit views at the navigation boundary that actually presents them. No usage or view is inferred from rendering. See the frontend telemetry contract.

Use Toggly's React Native SDK in React Native and Expo applications.

Grab the printable React Native cheat sheet (download PDF) — provider setup, hooks, components, storage, offline.

Session identity​

Supply instanceId as a provider prop or Core config field. Update identity and token props together, clearing instanceId at logout. Direct Core owners can await service.setContext({ identity: 'user-123', instanceId: 'minted-token' }); use empty strings to clear those fields and groups: [], claims: {} to clear targeting. await service.setIdentity(null) clears the token and restores the device identity. The provider's hook does not add a separate reporter. See session attribution.

Overview​

The React Native SDK is a modular feature flags solution designed for React Native and Expo applications. It provides React hooks, context providers, and declarative components for easy feature flag management.

Packages​

PackageDescription
@ops-ai/react-native-togglyMain SDK with React hooks and components
@ops-ai/react-native-toggly-coreCore functionality (automatically installed)
@ops-ai/react-native-toggly-storage-asyncAsyncStorage adapter for persistent caching
@ops-ai/react-native-toggly-storage-mmkvStorage adapter for MMKV 2 and 3
@ops-ai/react-native-toggly-storage-mmkv4Storage adapter for MMKV 4 and Nitro (1.0.0+)

Installation​

Main SDK (Required)​

npm install @ops-ai/react-native-toggly
# or
yarn add @ops-ai/react-native-toggly

Storage Adapters (Optional)​

For persistent feature flag caching, install a storage adapter:

AsyncStorage (Recommended for Expo):

npm install @ops-ai/react-native-toggly-storage-async @react-native-async-storage/async-storage

MMKV 4 with Nitro:

npm install @ops-ai/react-native-toggly-storage-mmkv4@^1.0.0 react-native-mmkv@^4.0.0 react-native-nitro-modules@^0.37.1
cd ios && pod install

MMKV 2/3 for existing hosts (MMKV 3 shown):

npm install @ops-ai/react-native-toggly-storage-mmkv react-native-mmkv@^3.0.0
cd ios && pod install

Both MMKV adapters require a native build; Expo Go is not supported. Expo apps need a development build. Select native dependencies compatible with your React Native release and its Node, iOS, Android, and Nitro requirements. See MMKV 4 setup and migration or the MMKV 2/3 adapter before changing an existing store.

Network State Detection (Optional)​

For automatic offline/online handling:

npm install @react-native-community/netinfo

Quick Start​

Basic Setup with Toggly.io​

import React from 'react';
import { TogglyProvider, Feature, useFeatureFlag } from '@ops-ai/react-native-toggly';
import { createAsyncStorageAdapter } from '@ops-ai/react-native-toggly-storage-async';

// Create storage adapter for persistent caching
const storage = createAsyncStorageAdapter();

function App() {
return (
<TogglyProvider
appKey="your-app-key"
environment="production"
storage={storage}
onError={(error) => {
// Report SDK failures to your app logger, Sentry, or Crashlytics
console.warn('Toggly error:', error);
}}
>
<MyApp />
</TogglyProvider>
);
}

function MyApp() {
const { isEnabled, isLoading } = useFeatureFlag('new-dashboard');

if (isLoading) {
return <LoadingSpinner />;
}

return (
<View>
<Feature featureKey="welcome-banner">
<WelcomeBanner />
</Feature>

{isEnabled && <NewDashboard />}
</View>
);
}

Setup without Toggly.io (Local Feature Flags)​

You can use the SDK without connecting to Toggly.io by providing feature defaults:

import React from 'react';
import { TogglyProvider, Feature } from '@ops-ai/react-native-toggly';

const featureDefaults = {
'new-dashboard': true,
'welcome-banner': true,
'experimental-feature': false,
};

function App() {
return (
<TogglyProvider featureDefaults={featureDefaults}>
<MyApp />
</TogglyProvider>
);
}

TogglyProvider​

The TogglyProvider component wraps your app and provides feature flag context to all child components.

<TogglyProvider
// Required (if using Toggly.io)
appKey="your-app-key"
environment="production"

// Optional
identity="user-123" // Stable user identifier
groups={['beta']} // Memberships used by targeting rules
claims={{ plan: 'pro' }} // String rule attributes
featureDefaults={{ feature1: true }} // Default values for features
storage={storageAdapter} // Storage adapter for caching
refreshInterval={30000} // Auto-refresh interval (ms)
hooks={[analyticsHook]} // Extensibility hooks

// Event callbacks
onReady={() => console.log('Ready')}
onError={(error) => console.error(error)}
onFlagsChanged={(flags) => console.log(flags)}
>
<App />
</TogglyProvider>

Supply these values on the first provider mount, instead of initializing and then calling a refreshing context setter. Startup captures the context before storage or native lifecycle callbacks can trigger a request. Persisted flags and conditional revisions are accepted only for the matching complete context.

An omitted or empty identity keeps the stored/generated device-ID fallback; empty groups/claims add no memberships or attributes. Use the context-update API for later login or membership changes, not as an extra startup step.

Configuration Options​

OptionTypeDescription
appKeystringYour Toggly.io application key
environmentstringEnvironment name (e.g., "production", "staging")
identitystringUnique user identifier for targeted rollouts
groupsstring[]Initial group memberships
claimsRecord<string, string>Initial string rule attributes
featureDefaultsRecord<string, boolean>Default feature flag values
storageTogglyStorageStorage adapter for persistent caching
maxCacheKeysnumber | nullOptional LRU cap for identity-scoped feature-flag cache keys (default: unlimited)
refreshIntervalnumberAuto-refresh interval in milliseconds
enableVariantsbooleanFetch /evaluated-variants-signed instead of /evaluated-signed and enable useVariant / getVariant / getVariantValue (default: false)
enableTelemetrybooleanCompact telemetry; defaults on when an app key is present
metricsBaseUrlstringIndependent collector; defaults to https://metrics.toggly.io
telemetryFlushIntervalMsnumberBase interval from 30,000 through 60,000 ms; defaults to 45,000
hooksHook[]Extensibility hooks for analytics, etc.
onReady() => voidCallback when SDK is initialized
onError(error: Error) => voidCallback when an error occurs
onFlagsChanged(flags: Record<string, boolean>) => voidCallback when flags change

For maxCacheKeys behavior, see Client-side cache limits.

Error reporting and refresh behavior​

Use onError to observe fetch, storage, signature, JWK, and refresh failures:

<TogglyProvider
appKey="your-app-key"
environment="production"
storage={storage}
onError={(error) => {
crashlytics().recordError(error);
}}
>
<App />
</TogglyProvider>

After one successful load, refresh failures preserve the last-known-good effective flags instead of clearing currently rendered UI. Initialization responses and debug state expose the last error, and feature UI updates through the effectiveFlagsChanged event. Feature, useFeatureFlag, and useFeatureGate subscribe to that event automatically.

For the shared reliability contract, see Reliability and Error Handling.

Entity context​

Pass a screen entity per widget or hook — not via setIdentity. See Entity & page context.

React Native is a client: registerContext is local. It does not call PUT sdk/{appKey}/contexts.

import { useTogglyService } from '@ops-ai/react-native-toggly'

type Order = { id: string; status: string }

// Call this hook inside a component under TogglyProvider.
function useOrderBadge() {
const toggly = useTogglyService()
return async (order: Order) => {
toggly.registerContext<Order>('Order', (value) => ({
kind: 'Order', key: value.id,
attributes: { Status: value.status },
}))
return toggly.isFeatureOn('OrderBadge', order, 'Order')
}
}

When a flag depends on an entity, resolve it per evaluation; without context, gates fail closed. setIdentity remains user.

Next Steps​