Skip to main content

Hooks

The React Native SDK provides several React hooks for evaluating feature flags.

useFeatureFlag, useFeatureGate, and the context from useToggly re-evaluate when remote flags refresh or when device-local post-filter gates change via notifyLocalGatesChanged(). See Post-filter gates and Advanced → Device-local post-filter gates.

useFeatureFlag​

Check a single feature flag status.

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

function MyComponent() {
const { isEnabled, isLoading, error } = useFeatureFlag('feature-key');

if (isLoading) return <Loading />;
if (error) return <Error message={error.message} />;

return isEnabled ? <NewFeature /> : <OldFeature />;
}

Return Value​

PropertyTypeDescription
isEnabledbooleanWhether the feature is enabled
isLoadingbooleanWhether the SDK is still loading
errorError | nullAny error that occurred

Options​

const { isEnabled } = useFeatureFlag('feature-key', {
defaultValue: false, // Value to use while loading
});

useFeatureGate​

Check multiple feature flags with all or any requirement.

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

function MyComponent() {
// All features must be enabled
const { isEnabled: allEnabled } = useFeatureGate(
['feature1', 'feature2'],
{ requirement: 'all' }
);

// At least one feature must be enabled
const { isEnabled: anyEnabled } = useFeatureGate(
['feature1', 'feature2'],
{ requirement: 'any' }
);

// Negate the result (show when features are OFF)
const { isEnabled: noneEnabled } = useFeatureGate(
['feature1', 'feature2'],
{ requirement: 'all', negate: true }
);
}

Options​

OptionTypeDefaultDescription
requirement'all' | 'any''all'Whether all or any features must be enabled
negatebooleanfalseInverts the result
defaultValuebooleanfalseValue while loading

useToggly​

Access the full Toggly service for advanced operations.

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

function MyComponent() {
const {
toggly, // TogglyService instance
isReady, // SDK is initialized
isLoading, // Currently loading
error, // Initialization error
isFeatureOn, // Function to check feature
setIdentity, // Set user identity
refresh, // Manually refresh flags
getVariant, // Assigned variant { name, configurationValue } | null
getVariantValue, // Assigned variant's configurationValue, or null
recordUsage, // Explicit feature use
recordView, // Explicit feature view
incrementCounter, // App-level counter
setGauge, // App-level latest value
flushTelemetry, // Await current best-effort drain
} = useToggly();

const handleLogin = async (userId: string) => {
await setIdentity(userId);
};

const handleLogout = async () => {
await setIdentity(null);
};

const handleRefresh = async () => {
await refresh();
};

// Imperative feature check
const isPremium = isFeatureOn('premium-features');
}

Return Value​

PropertyTypeDescription
togglyTogglyServiceThe underlying Toggly service instance
isReadybooleanWhether the SDK is fully initialized
isLoadingbooleanWhether the SDK is currently loading
errorError | nullAny initialization error
isFeatureOn(key: string) => booleanCheck if a feature is enabled
setIdentity(id: string | null) => Promise<void>Set or clear user identity
refresh() => Promise<void>Manually refresh feature flags
getVariant(key: string) => { name: string; configurationValue?: unknown } | nullAssigned variant for a feature (requires enableVariants); null when disabled, off, or unassigned
getVariantValue(key: string) => unknown | nullShortcut for getVariant(key)?.configurationValue ?? null
recordUsage / recordView(key: string, variant?: string) => voidRecord an explicit feature event without evaluating it
incrementCounter(name: string, delta?: number) => voidAdd a nonnegative integer delta; default 1
setGauge(name: string, value: number) => voidKeep the latest finite nonnegative value
flushTelemetry() => Promise<void>Drain current queued work on a best-effort basis

useVariant​

Read the assigned variant for a feature. Requires enableVariants: true on TogglyProvider (or the underlying TogglyService config), which switches the SDK to fetch /evaluated-variants-signed instead of /evaluated-signed. Re-renders after feature definitions refresh or device-local gates change, the same way useFeatureFlag does.

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

function App() {
return (
<TogglyProvider appKey="your-app-key" environment="Production" enableVariants>
<PricingScreen />
</TogglyProvider>
);
}

function PricingScreen() {
const variant = useVariant('pricing-test');

if (variant?.name === 'treatment') {
return <NewPricing config={variant.configurationValue} />;
}
return <ControlPricing />;
}

useVariant returns null when enableVariants is off, the feature is disabled, or no variant data has loaded yet — treat that the same as the control/default experience.

Return Value​

TypeDescription
{ name: string; configurationValue?: unknown } | nullThe assigned variant name and its configuration payload, or null

Examples​

Conditional Rendering​

function CheckoutButton() {
const { isEnabled: useNewCheckout, isLoading } = useFeatureFlag('new-checkout');

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

return useNewCheckout ? (
<NewCheckoutButton onPress={handleNewCheckout} />
) : (
<ClassicCheckoutButton onPress={handleClassicCheckout} />
);
}

Combined Feature Gates​

function PremiumContent() {
const { isEnabled: hasPremiumAccess } = useFeatureGate(
['premium-subscription', 'beta-tester'],
{ requirement: 'any' } // User has premium OR is beta tester
);

if (!hasPremiumAccess) {
return <UpgradePrompt />;
}

return <PremiumContentView />;
}

User Login/Logout​

function AuthButtons() {
const { setIdentity, refresh } = useToggly();
const [user, setUser] = useState(null);

const handleLogin = async (credentials) => {
const userData = await loginAPI(credentials);
setUser(userData);

// Set identity for targeted feature rollouts
await setIdentity(userData.id);
};

const handleLogout = async () => {
await logoutAPI();
setUser(null);

// Clear identity
await setIdentity(null);
};

return (
<View>
{user ? (
<Button title="Logout" onPress={handleLogout} />
) : (
<Button title="Login" onPress={() => handleLogin(credentials)} />
)}
</View>
);
}

Manual Refresh​

function SettingsScreen() {
const { refresh } = useToggly();
const [refreshing, setRefreshing] = useState(false);

const handleRefresh = async () => {
setRefreshing(true);
try {
await refresh();
} finally {
setRefreshing(false);
}
};

return (
<ScrollView
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />
}
>
{/* Content */}
</ScrollView>
);
}

Entity context​

Pass the entity into useFeatureFlag / useToggly().isFeatureOn per call. Do not put entity attributes on setIdentity. See Entity & page context.

const { enabled } = useFeatureFlag('OrderBadge', { context: order, contextKind: 'Order' })

Clients register mappers locally only. Missing context fails closed.

Best Practices​

  1. Use useFeatureFlag for single flags - It's optimized for single feature checks
  2. Use useFeatureGate for multiple flags - When you need AND/OR logic
  3. Use useToggly for advanced operations - Identity management, manual refresh
  4. Handle loading states - Show appropriate UI while flags are loading
  5. Provide defaults - Use featureDefaults for offline scenarios