π§ Detect Android navigation mode (3-button, 2-button, or gesture navigation) with native precision using Turbo modules.
![]() |
![]() |
- π― Direct Native Detection - No hacky workarounds or dimension-based guessing
- β‘ Turbo Module - Built for React Native New Architecture
- π Real-time Detection - Accurate navigation mode identification
- π Navigation Bar Height - Get exact navigation bar height in dp for precise UI calculations
- π± Cross Platform - Android detection + iOS compatibility
- π£ React Hooks - Easy integration with
useNavigationMode()
- π¦ Zero Dependencies - Lightweight and performant
- π‘οΈ TypeScript - Full type safety out of the box
βοΈ Edge To Edge Support - Full support forreact-native-edge-to-edge
- π² Expo Support - Works with Expo SDK 52+ managed workflow and development builds
Using yarn:
yarn add react-native-navigation-mode
Using npm:
npm install react-native-navigation-mode
Note: Auto-linking should handle setup automatically for all newer RN versions.
npx expo install react-native-navigation-mode
Add the plugin to your app.json
or app.config.ts
:
{
"expo": {
"plugins": [
"react-native-navigation-mode"
]
}
}
For bare workflow or custom native code, you'll need to prebuild:
npx expo prebuild
Since this library contains native code, it requires a custom development build. You cannot use it with standard Expo Go.
- React Native: 0.77.0+
- Expo SDK: 52+ (for managed workflow)
- Android: API 21+ (Android 5.0+)
- iOS: Any version (returns gesture navigation)
- New Architecture: Required (enabled by default in RN 0.77+ and Expo SDK 52+)
import { useNavigationMode } from 'react-native-navigation-mode';
export default function App() {
const { navigationMode, loading, error } = useNavigationMode();
if (loading) return (<Text>Detecting navigation mode...</Text>);
if (error) return (<Text>Error: {error.message}</Text>);
return (
<View>
<Text>Navigation Type: {navigationMode?.type}</Text>
<Text>Gesture Navigation: {navigationMode?.isGestureNavigation ? 'Yes' : 'No'}</Text>
<Text>Navigation Bar Height: {navigationMode?.navigationBarHeight}dp</Text>
</View>
);
}
- Returned property types:
Property | Type | Description |
---|---|---|
navigatioMode | NavigationModeInfo or null |
All properties mentioned in NavigationModeInfo. |
loading | boolean |
Indicates if navigation mode info is being fetched. |
error | Error |
Typescript error object containing the cause of the error. |
The easiest way to detect navigation mode with loading and error states.
import { useNavigationMode } from 'react-native-navigation-mode';
function MyComponent() {
const { navigationMode, loading, error } = useNavigationMode();
if (loading) return <Text>Loading...</Text>;
if (error) return <Text>Error: {error.message}</Text>;
return (
<View>
<Text>Navigation Type: {navigationMode?.type}</Text>
<Text>Is Gesture: {navigationMode?.isGestureNavigation ? 'Yes' : 'No'}</Text>
<Text>Bar Height: {navigationMode?.navigationBarHeight}dp</Text>
</View>
);
}
getNavigationMode(): Promise<
NavigationModeInfo>
Returns comprehensive navigation mode information.
import { getNavigationMode } from 'react-native-navigation-mode';
const navInfo = await getNavigationMode();
console.log('Navigation type:', navInfo.type); // '3_button', '2_button', 'gesture', or 'unknown'
Quick check if device is using gesture navigation.
import { isGestureNavigation } from 'react-native-navigation-mode';
const isGesture = await isGestureNavigation();
console.log('Gesture navigation:', isGesture); // true/false
Returns the navigation bar height in density-independent pixels (dp).
import { getNavigationBarHeight } from 'react-native-navigation-mode';
const height = await getNavigationBarHeight();
console.log('Navigation bar height:', height); // number (dp)
Property | Type | Description |
---|---|---|
type | '3_button' or '2_button' or 'gesture' or 'unknown' |
4 possible Android navigation modes that can be detected. |
isGestureNavigation | boolean |
Whether gesture navigation is active. |
interactionMode | number or undefined |
See Interaction Mode Values |
navigationBarHeight | number or undefined |
Navigation bar height in density-independent pixels (dp). |
Android Mode | Type | Description |
---|---|---|
0 | 3_button |
Traditional Android navigation (Back, Home, Recent) |
1 | 2_button |
Two-button navigation (Back, Home) |
2 | gesture |
Full gesture navigation |
-1 | unknown |
Could not determine navigation mode |
import { useNavigationMode } from 'react-native-navigation-mode';
export default function AdaptiveUI() {
const { navigationMode } = useNavigationMode();
return (
<View
style={{
// paddingBottom using real navigation bar height
paddingBottom: navigationMode?.navigationBarHeight || 0,
}}>
{/* Your content */}
</View>
);
}
import { useNavigationMode } from 'react-native-navigation-mode';
export default function ConditionalUI() {
const { navigationMode } = useNavigationMode();
return (
<View>
{navigationMode?.isGestureNavigation && (
<Text>Swipe gestures are available!</Text>
)}
{navigationMode?.type === '3_button' && (
<Text>Traditional navigation buttons detected</Text>
)}
</View>
);
}
import {
getNavigationMode,
isGestureNavigation,
getNavigationBarHeight
} from 'react-native-navigation-mode';
const checkNavigation = async () => {
// Get all info at once
const navInfo = await getNavigationMode();
// Or get specific info
const isGesture = await isGestureNavigation();
const barHeight = await getNavigationBarHeight();
console.log('Navigation info:', navInfo);
console.log('Is gesture:', isGesture);
console.log('Bar height:', barHeight);
};
Android devices can use different navigation modes, but detecting which one is active has been a major pain point for React Native developers. Most existing solutions rely on unreliable workarounds:
- Screen dimension calculations - Breaks on different screen sizes and orientations
- Safe area inset guessing - Inconsistent across devices and Android versions
- Margin-based detection - Fragile and depends on UI layout changes
- Manual device databases - Impossible to maintain for all Android devices
This library uses official Android APIs to directly query the system's navigation configuration:
config_navBarInteractionMode
- The actual system resource Android uses internally- Settings.Secure provider - Fallback method for reliable detection
- WindowInsets API - Accurate navigation bar height detection
- Zero guesswork - No calculations, no assumptions, just direct system queries
With Android 15 enforcing edge-to-edge display for apps targeting API 35 and Google mandating this for Play Store updates starting August 31, 2025, proper navigation detection is now essential:
- Edge-to-edge enforcement - Android 16 will remove the opt-out entirely
- Expo SDK 52+ - New projects use edge-to-edge by default
- React Native 0.79+ - Built-in support for 16KB page size and edge-to-edge
- Safe area management - Critical for preventing content overlap with system bars
// Before: Unreliable dimension-based guessing
const isGesture = screenHeight === windowHeight; // π’ Breaks easily
// After: Direct system detection
const isGesture = await isGestureNavigation(); // π― Always accurate
Perfect for:
- π¨ Adaptive UI layouts based on navigation type
- π± Bottom sheet positioning and safe areas
- π§ Navigation-aware component design
- π Edge-to-edge layout compatibility
- π Analytics and user experience tracking
Platform | Support | Notes |
---|---|---|
Android | β Full | Detects all navigation modes and navigation bar height via native Android APIs |
iOS | β Compatible | Always returns gesture and navigationBarHeight: 0 (iOS uses gesture navigation) |
Expo | β Full | Supported in managed workflow with SDK 52+ |
- API 21+ - Basic navigation bar detection
- API 29+ - Full navigation mode detection (
config_navBarInteractionMode
) - All versions - Fallback detection methods included
- API 30+ - WindowInsets-based navigation bar height detection
- API 24-29 - Resource-based navigation bar height fallback
The library uses multiple detection methods for maximum accuracy:
config_navBarInteractionMode
- Official Android configuration (API 29+)- Settings Provider - Checks
navigation_mode
system setting - WindowInsets API - Accurate navigation bar height detection (API 30+)
- Resource-based fallback - Navigation bar height for older devices
- π iOS Behavior - iOS always returns
isGestureNavigation: true
andnavigationBarHeight: 0
since iOS doesn't have Android-style navigation bars - β‘ Performance - Turbo module ensures minimal performance impact
- π Real-time - Navigation mode is detected at call time, reflecting current device settings
"TurboModuleRegistry.getEnforcing(...) is not a function"
- Ensure you're using React Native 0.68+ with new architecture enabled
- For older RN versions, the module will fallback gracefully
Always returns 'unknown'
on Android
- Check if your device/emulator supports the navigation mode APIs
- Some custom ROMs may not expose standard Android navigation settings
Navigation bar height returns 0
- This is normal on devices without navigation bars (some tablets)
- On older Android versions, fallback detection may not work on all devices
Expo: "Package does not contain a valid config plugin"
- Ensure you've installed the latest version of the library
- Try clearing your cache:
npx expo start --clear
- Make sure the plugin is added to your
app.json
See the contributing guide to learn how to contribute to the repository and the development workflow.
MIT
Β Β Β Β Β Β Β Β Β
Β Β Β Β Β Β Β Β Β
- Module built using create-react-native-library
- Readme is edited using Typora