Detect Android navigation mode (3-button, 2-button, or gesture)
npm install react-native-navigation-modeš§ Detect Android navigation mode (3-button, 2-button, or gesture navigation) with native precision using Turbo modules.
   !Android !iOS  !TS !Turbo Module !Expo !npm bundle size 
![]() | ![]() |
Traditional Android navigation | Home + Back buttons | Swipe-based navigation |
------
- šÆ 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 for react-native-edge-to-edge
- š² Expo Support - Works with Expo SDK 52+ managed workflow and development builds
#### React Native (Bare Workflow)
Using yarn:
``sh`
yarn add react-native-navigation-mode
Using npm:
`sh`
npm install react-native-navigation-mode
> Note: Auto-linking should handle setup automatically for all newer RN versions.
#### Expo (Managed Workflow)
`sh`
npx expo install react-native-navigation-mode
##### Development Builds
Since this library contains native code, it requires a development build. It cannot be used with Expo Go.
`shUsing EAS Build (recommended)
eas build --profile development --platform android
#### Requirements
- 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+)
---
$3
`tsx
import { useNavigationMode } from 'react-native-navigation-mode';export default function App() {
const { navigationMode, loading, error } = useNavigationMode();
if (loading) return (Detecting navigation mode... );
if (error) return (Error: {error.message} );
return (
Navigation Type: {navigationMode?.type}
Gesture Navigation: {navigationMode?.isGestureNavigation ? 'Yes' : 'No'}
Navigation Bar Height: {navigationMode?.navigationBarHeight}dp
);
}
`---
š§ API Reference
$3
####
useNavigationMode(): { navigationMode, loading, error }- 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.
`tsx
import { useNavigationMode } from 'react-native-navigation-mode';function MyComponent() {
const { navigationMode, loading, error } = useNavigationMode();
if (loading) return Loading... ;
if (error) return Error: {error.message} ;
return (
Navigation Type: {navigationMode?.type}
Is Gesture: {navigationMode?.isGestureNavigation ? 'Yes' : 'No'}
Bar Height: {navigationMode?.navigationBarHeight}dp
);
}
`$3
####
getNavigationMode(): Promise<NavigationModeInfo>Returns comprehensive navigation mode information.
`typescript
import { getNavigationMode } from 'react-native-navigation-mode';const navInfo = await getNavigationMode();
console.log('Navigation type:', navInfo.type); // '3_button', '2_button', 'gesture', or 'unknown'
`####
isGestureNavigation(): PromiseQuick check if device is using gesture navigation.
`typescript
import { isGestureNavigation } from 'react-native-navigation-mode';const isGesture = await isGestureNavigation();
console.log('Gesture navigation:', isGesture); // true/false
`####
getNavigationBarHeight(): PromiseReturns the navigation bar height in density-independent pixels (dp).
`typescript
import { getNavigationBarHeight } from 'react-native-navigation-mode';const height = await getNavigationBarHeight();
console.log('Navigation bar height:', height); // number (dp)
`$3
####
NavigationModeInfo| 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 | Navigation bar height in density-independent pixels (dp). 0 for iOS |$3
Only available for Android 10 (API 29) or newer.
| 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 |---
š” Usage Examples
$3
`tsx
import { useNavigationMode } from 'react-native-navigation-mode';export default function AdaptiveUI() {
const { navigationMode } = useNavigationMode();
return (
style={{
// paddingBottom using real navigation bar height
paddingBottom: navigationMode?.navigationBarHeight || 0,
}}>
{/ Your content /}
);
}
`$3
`tsx
import { useNavigationMode } from 'react-native-navigation-mode';export default function ConditionalUI() {
const { navigationMode } = useNavigationMode();
return (
{navigationMode?.isGestureNavigation && (
Swipe gestures are available!
)}
{navigationMode?.type === '3_button' && (
Traditional navigation buttons detected
)}
);
}
`$3
`typescript
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);
};
`---
š¤ Why This Library?
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:
$3
- 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
$3
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$3
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
$3
`typescript
// 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
---
š ļø Technical Details
$3
| 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+ |$3
- 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$3
The library uses multiple detection methods for maximum accuracy:
1.
config_navBarInteractionMode - Official Android configuration (API 29+)
2. Settings Provider - Checks navigation_mode system setting
3. WindowInsets API - Accurate navigation bar height detection (API 30+)
4. Resource-based fallback - Navigation bar height for older devices$3
1. š iOS Behavior - iOS always returns
isGestureNavigation: true and navigationBarHeight: 0 since iOS doesn't have Android-style navigation bars
2. ā” Performance - Turbo module ensures minimal performance impact
3. š Real-time - Navigation mode is detected at call time, reflecting current device settings---
š Troubleshooting
$3
"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
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
---