Components
The React Native SDK provides declarative components for conditional rendering based on feature flags.
Each committed component evaluation records the effective checked leaves when
frontend telemetry is enabled. Internal snapshot projection is silent, and
rendering never implies a usage or view event. Record those events explicitly
through useToggly.
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
| Prop | Type | Default | Description |
|---|---|---|---|
featureKey | string | - | Single feature key to check |
featureKeys | string[] | - | Multiple feature keys to check |
requirement | 'all' | 'any' | 'all' | Whether all or any features must be enabled |
negate | boolean | false | Render children when the feature is off |
loading | ReactNode | - | Content to show while loading |
children | ReactNode | - | 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
- Use meaningful feature keys - Names like
new-checkoutare better thanfeature-123 - Pair on and off with
negate- Use a second<Feature negate>for the alternate UI - Keep feature trees shallow - Don't nest too many Feature components
- Consider loading states - Show appropriate loading UI while flags are being fetched
- Clean up old features - Remove Feature components once features are fully rolled out