Native navigation primitives for React Native apps with native screen management and transitions.
—
Beta and experimental components for advanced navigation patterns including bottom tabs, split views, and experimental screen hosting. These features are under active development and may change in future versions.
import {
BottomTabs,
BottomTabsScreen,
ScreenStackHost,
StackScreen,
StackScreenLifecycleState,
SplitViewHost,
SplitViewScreen
} from "react-native-screens";Experimental native bottom tabs component that provides platform-native tab bar functionality with improved performance over JavaScript-based implementations.
/**
* Experimental native bottom tabs component
*/
function BottomTabs(props: BottomTabsProps): JSX.Element;
interface BottomTabsProps {
/** Currently selected tab index */
selectedTab?: number;
/** Callback when tab selection changes */
onTabChange?: (event: NativeSyntheticEvent<NativeFocusChangeEvent>) => void;
/** Whether tabs should be scrollable (Android) */
scrollable?: boolean;
/** Tab bar background color */
barTintColor?: ColorValue;
/** Selected tab tint color */
selectedItemColor?: ColorValue;
/** Unselected tab tint color */
unselectedItemColor?: ColorValue;
/** Tab bar style (iOS) */
barStyle?: 'default' | 'black';
/** Whether tab bar is translucent (iOS) */
translucent?: boolean;
/** Tab bar appearance (iOS) */
appearance?: 'default' | 'opaque';
/** Content inset adjustment behavior (iOS) */
contentInsetAdjustmentBehavior?: 'automatic' | 'scrollableAxes' | 'never' | 'always';
}
interface NativeFocusChangeEvent {
/** Index of the newly focused tab */
focusedTab: number;
/** Index of the previously focused tab */
previouslyFocusedTab: number;
}Usage Example:
import React, { useState } from 'react';
import { BottomTabs, BottomTabsScreen } from 'react-native-screens';
import { View, Text } from 'react-native';
function TabNavigator() {
const [selectedTab, setSelectedTab] = useState(0);
const handleTabChange = (event) => {
setSelectedTab(event.nativeEvent.focusedTab);
};
return (
<BottomTabs
selectedTab={selectedTab}
onTabChange={handleTabChange}
barTintColor="#f8f9fa"
selectedItemColor="#007AFF"
unselectedItemColor="#8E8E93"
>
<BottomTabsScreen
title="Home"
tabBarIcon="home"
badge="3"
>
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home Tab Content</Text>
</View>
</BottomTabsScreen>
<BottomTabsScreen
title="Search"
tabBarIcon="search"
>
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Search Tab Content</Text>
</View>
</BottomTabsScreen>
<BottomTabsScreen
title="Profile"
tabBarIcon="person"
>
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Profile Tab Content</Text>
</View>
</BottomTabsScreen>
</BottomTabs>
);
}Individual screen component within the bottom tabs navigator that represents a single tab with its content and configuration.
/**
* Individual screen within bottom tabs
*/
function BottomTabsScreen(props: BottomTabsScreenProps): JSX.Element;
interface BottomTabsScreenProps {
/** Tab title */
title?: string;
/** Tab bar icon name or component */
tabBarIcon?: string | React.ComponentType<any>;
/** Badge text to display on tab */
badge?: string;
/** Badge background color */
badgeColor?: ColorValue;
/** Whether tab is enabled */
enabled?: boolean;
/** Tab tint color when selected */
activeTintColor?: ColorValue;
/** Tab tint color when unselected */
inactiveTintColor?: ColorValue;
/** Tab background color */
backgroundColor?: ColorValue;
/** Whether tab should show title */
showTitle?: boolean;
/** Custom tab bar component */
tabBarComponent?: React.ComponentType<any>;
/** Tab press callback */
onTabPress?: () => void;
/** Tab long press callback */
onTabLongPress?: () => void;
}Advanced Usage:
import React from 'react';
import { BottomTabsScreen } from 'react-native-screens';
import { Image } from 'react-native';
function CustomTabIcon({ focused, color }) {
return (
<Image
source={focused ? require('./home-filled.png') : require('./home-outline.png')}
style={{ width: 24, height: 24, tintColor: color }}
/>
);
}
function CustomBottomTabsScreen() {
return (
<BottomTabsScreen
title="Home"
tabBarIcon={CustomTabIcon}
badge="5"
badgeColor="#FF3B30"
onTabPress={() => console.log('Home tab pressed')}
onTabLongPress={() => console.log('Home tab long pressed')}
>
{/* Screen content */}
</BottomTabsScreen>
);
}Experimental screen stack host component that provides advanced screen stack management with enhanced lifecycle control.
/**
* Experimental screen stack host component
*/
function ScreenStackHost(props: ViewProps): JSX.Element;Usage Example:
import React from 'react';
import { ScreenStackHost, StackScreen } from 'react-native-screens';
function ExperimentalStackNavigator() {
return (
<ScreenStackHost>
<StackScreen
active={1}
lifecycle={StackScreenLifecycleState.Active}
>
{/* Screen content */}
</StackScreen>
</ScreenStackHost>
);
}Experimental stack screen component with enhanced lifecycle management and performance optimizations.
/**
* Experimental stack screen component
*/
function StackScreen(props: StackScreenProps): JSX.Element;
interface StackScreenProps extends ViewProps {
/** Screen active state */
active?: 0 | 1;
/** Screen lifecycle state */
lifecycle?: StackScreenLifecycleState;
/** Screen identifier */
screenId?: string;
/** Performance optimization settings */
optimizationHints?: {
/** Whether screen content should be preloaded */
preload?: boolean;
/** Whether screen should be kept in memory */
keepAlive?: boolean;
/** Priority level for resource allocation */
priority?: 'low' | 'normal' | 'high';
};
}Enumeration of lifecycle states for stack screens, providing fine-grained control over screen behavior.
/**
* Lifecycle states for stack screens
*/
enum StackScreenLifecycleState {
/** Screen is not yet initialized */
Uninitialized = 0,
/** Screen is being created */
Creating = 1,
/** Screen is active and visible */
Active = 2,
/** Screen is inactive but still in memory */
Inactive = 3,
/** Screen is being destroyed */
Destroying = 4,
/** Screen has been destroyed */
Destroyed = 5
}Usage Example:
import React, { useEffect } from 'react';
import { StackScreen, StackScreenLifecycleState } from 'react-native-screens';
function LifecycleAwareScreen({ lifecycle }) {
useEffect(() => {
switch (lifecycle) {
case StackScreenLifecycleState.Creating:
console.log('Screen is being created');
break;
case StackScreenLifecycleState.Active:
console.log('Screen is now active');
break;
case StackScreenLifecycleState.Inactive:
console.log('Screen is now inactive');
break;
case StackScreenLifecycleState.Destroying:
console.log('Screen is being destroyed');
break;
}
}, [lifecycle]);
return (
<StackScreen
lifecycle={lifecycle}
optimizationHints={{
preload: true,
keepAlive: false,
priority: 'normal'
}}
>
{/* Screen content */}
</StackScreen>
);
}Host component for split view layouts, primarily designed for iPad and larger screens to provide master-detail interfaces.
/**
* Host component for split view layouts (iPad)
*/
function SplitViewHost(props: SplitViewHostProps): JSX.Element;
interface SplitViewHostProps extends ViewProps {
/** Split view display mode */
displayMode?: SplitViewDisplayMode;
/** Primary column width */
primaryColumnWidth?: number;
/** Secondary column width */
secondaryColumnWidth?: number;
/** Minimum primary column width */
minimumPrimaryColumnWidth?: number;
/** Maximum primary column width */
maximumPrimaryColumnWidth?: number;
/** Preferred display mode */
preferredDisplayMode?: SplitViewDisplayMode;
/** Whether primary column is shown */
showsPrimaryColumn?: boolean;
/** Whether secondary column is shown */
showsSecondaryColumn?: boolean;
/** Callback when display mode changes */
onDisplayModeChange?: (event: NativeSyntheticEvent<{ displayMode: SplitViewDisplayMode }>) => void;
/** Callback when column visibility changes */
onColumnVisibilityChange?: (event: NativeSyntheticEvent<{
primaryVisible: boolean;
secondaryVisible: boolean;
}>) => void;
}Usage Example:
import React, { useState } from 'react';
import { SplitViewHost, SplitViewScreen } from 'react-native-screens';
import { View, Text, FlatList, TouchableOpacity } from 'react-native';
function MasterDetailInterface() {
const [selectedItem, setSelectedItem] = useState(null);
const [displayMode, setDisplayMode] = useState('automatic');
const handleDisplayModeChange = (event) => {
setDisplayMode(event.nativeEvent.displayMode);
};
return (
<SplitViewHost
displayMode={displayMode}
primaryColumnWidth={320}
minimumPrimaryColumnWidth={250}
maximumPrimaryColumnWidth={400}
onDisplayModeChange={handleDisplayModeChange}
>
<SplitViewScreen column="primary">
<View style={{ flex: 1, backgroundColor: '#f5f5f5' }}>
<Text style={{ fontSize: 18, padding: 16 }}>Master</Text>
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() => setSelectedItem(item)}
style={{
padding: 16,
backgroundColor: selectedItem?.id === item.id ? '#007AFF' : 'white'
}}
>
<Text style={{
color: selectedItem?.id === item.id ? 'white' : 'black'
}}>
{item.title}
</Text>
</TouchableOpacity>
)}
/>
</View>
</SplitViewScreen>
<SplitViewScreen column="secondary">
<View style={{ flex: 1, backgroundColor: 'white', padding: 16 }}>
<Text style={{ fontSize: 18, marginBottom: 16 }}>Detail</Text>
{selectedItem ? (
<View>
<Text style={{ fontSize: 16, fontWeight: 'bold' }}>
{selectedItem.title}
</Text>
<Text style={{ marginTop: 8 }}>
{selectedItem.description}
</Text>
</View>
) : (
<Text style={{ color: '#8E8E93' }}>
Select an item from the master list
</Text>
)}
</View>
</SplitViewScreen>
</SplitViewHost>
);
}Individual screen component within a split view layout, representing either the primary or secondary column content.
/**
* Individual screen within split view
*/
function SplitViewScreen(props: SplitViewScreenProps): JSX.Element;
interface SplitViewScreenProps extends ViewProps {
/** Which column this screen represents */
column: 'primary' | 'secondary';
/** Whether this column is currently visible */
visible?: boolean;
/** Column background color */
backgroundColor?: ColorValue;
/** Column border configuration */
borderColor?: ColorValue;
borderWidth?: number;
/** Callback when column becomes visible */
onAppear?: () => void;
/** Callback when column becomes hidden */
onDisappear?: () => void;
}type SplitViewDisplayMode =
| 'automatic' // System determines layout
| 'secondaryOnly' // Show only secondary column
| 'oneBesideSecondary' // Primary beside secondary
| 'oneOverSecondary' // Primary over secondary
| 'twoBesideSecondary' // Two columns beside secondary
| 'twoOverSecondary' // Two columns over secondary
| 'twoDisplaceSecondary'; // Two columns displacing secondaryimport { featureFlags } from "react-native-screens";
// Enable experimental bottom tabs
featureFlags.experiment.controlledBottomTabs = true;
// Check if experimental features are available
if (featureFlags.experiment.controlledBottomTabs) {
// Use experimental BottomTabs component
} else {
// Use alternative implementation
}// Import private debugging utilities (use with caution)
import {
internalEnableDetailedBottomTabsLogging,
bottomTabsDebugLog
} from "react-native-screens/private";
// Enable detailed logging for debugging (development only)
if (__DEV__) {
internalEnableDetailedBottomTabsLogging(true);
// Custom debug logging
bottomTabsDebugLog('Custom debug message', { data: 'value' });
}// Gradual migration approach
import { featureFlags, BottomTabs } from "react-native-screens";
function ConditionalBottomTabs() {
// Check if experimental features are stable enough
const useExperimental = featureFlags.experiment.controlledBottomTabs &&
Platform.OS === 'ios' && // Start with iOS only
!__DEV__; // Production ready
if (useExperimental) {
return <BottomTabs {...props} />;
}
// Fallback to stable implementation
return <LegacyBottomTabs {...props} />;
}Install with Tessl CLI
npx tessl i tessl/npm-react-native-screens