Skip to main content

Components

The React Native SDK provides declarative components for conditional rendering based on feature flags.

Feature Component​

Conditionally renders content when a feature is on. Set negate to render when it is off — the same pattern as Next.js and .NET.

Basic Usage​

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

// Show when enabled
<Feature featureKey="new-feature">
<NewFeatureContent />
</Feature>

// Show when disabled
<Feature featureKey="new-feature" negate>
<LegacyFeatureContent />
</Feature>

Props​

PropTypeDefaultDescription
featureKeystring-Single feature key to check
featureKeysstring[]-Multiple feature keys to check
requirement'all' | 'any''all'Whether all or any features must be enabled
negatebooleanfalseRender children when the feature is off
loadingReactNode-Content to show while loading
childrenReactNode-Content when the gate passes

Examples​

Multiple Features (All Required)​

<Feature featureKeys={['feature1', 'feature2']}>
<Text>Shown when BOTH features are enabled</Text>
</Feature>

Multiple Features (Any Required)​

<Feature featureKeys={['feature1', 'feature2']} requirement="any">
<Text>Shown when AT LEAST ONE feature is enabled</Text>
</Feature>

On and Off Paths​

<Feature featureKey="premium-feature">
<PremiumContent />
</Feature>
<Feature featureKey="premium-feature" negate>
<UpgradePrompt />
</Feature>

With Loading State​

<Feature
featureKey="new-feature"
loading={<SkeletonLoader />}
>
<NewFeatureContent />
</Feature>

Complex Example​

<Feature
featureKeys={['premium-subscription', 'advanced-analytics']}
requirement="all"
loading={<ActivityIndicator size="large" />}
>
<AdvancedAnalyticsDashboard />
</Feature>
<Feature
featureKeys={['premium-subscription', 'advanced-analytics']}
requirement="all"
negate
loading={<ActivityIndicator size="large" />}
>
<View style={styles.upgradeCard}>
<Text>Upgrade to Premium for advanced analytics</Text>
<Button title="Upgrade Now" onPress={handleUpgrade} />
</View>
</Feature>

Comparison: Component vs Hook​

Choose the right approach for your use case:

Use Feature Component When:​

  • You want declarative, readable JSX
  • The feature controls visibility of entire component trees
  • You need built-in loading handling
<Feature featureKey="new-dashboard">
<NewDashboard />
</Feature>
<Feature featureKey="new-dashboard" negate>
<OldDashboard />
</Feature>

Use useFeatureFlag Hook When:​

  • You need the flag value for logic beyond rendering
  • You want more control over the component behavior
  • You're using the flag in event handlers or effects
function DashboardScreen() {
const { isEnabled } = useFeatureFlag('new-dashboard');

useEffect(() => {
if (isEnabled) {
analytics.track('new_dashboard_viewed');
}
}, [isEnabled]);

return isEnabled ? <NewDashboard /> : <OldDashboard />;
}

Patterns​

Feature-Gated Navigation​

function BottomNavigation() {
return (
<TabNavigator>
<Tab name="Home" component={HomeScreen} />
<Tab name="Search" component={SearchScreen} />

<Feature featureKey="social-features">
<Tab name="Friends" component={FriendsScreen} />
</Feature>

<Tab name="Profile" component={ProfileScreen} />
</TabNavigator>
);
}

Feature-Gated Form Fields​

function SettingsForm() {
return (
<Form>
<TextField name="name" label="Name" />
<TextField name="email" label="Email" />

<Feature featureKey="advanced-settings">
<TextField name="apiKey" label="API Key" />
<Switch name="darkMode" label="Dark Mode" />
</Feature>

<Button type="submit">Save</Button>
</Form>
);
}

A/B Testing UI Variants​

function HeroSection() {
return (
<View>
<Feature featureKey="hero-variant-a">
<HeroVariantA />
</Feature>

<Feature featureKey="hero-variant-b">
<HeroVariantB />
</Feature>

{/* Default when neither variant is enabled */}
<Feature
featureKeys={['hero-variant-a', 'hero-variant-b']}
requirement="any"
negate
>
<DefaultHero />
</Feature>
</View>
);
}

Gradual Rollout​

function CheckoutFlow() {
return (
<>
<Feature
featureKey="new-checkout-flow"
loading={<CheckoutSkeleton />}
>
<NewCheckout />
</Feature>
<Feature
featureKey="new-checkout-flow"
negate
loading={<CheckoutSkeleton />}
>
<LegacyCheckout />
</Feature>
</>
);
}

Entity context​

<Feature featureKey="OrderBadge" context={order} contextKind="Order">
<Badge />
</Feature>

Entity is per widget. setIdentity is user. Clients do not register schemas. See Entity & page context.

Best Practices​

  1. Use meaningful feature keys - Names like new-checkout are better than feature-123
  2. Pair on and off with negate - Use a second <Feature negate> for the alternate UI
  3. Keep feature trees shallow - Don't nest too many Feature components
  4. Consider loading states - Show appropriate loading UI while flags are being fetched
  5. Clean up old features - Remove Feature components once features are fully rolled out