react-native-navigation-mode
v1.2.5
Published
Detect Android navigation mode (3-button, 2-button, or gesture)
Maintainers
Readme
react-native-navigation-mode
🧭 Detect Android navigation mode (3-button, 2-button, or gesture navigation) with native precision using Turbo modules.
✨ Key Features
- 🎯 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
🚀 Quick Start
Installation
React Native (Bare Workflow)
Using yarn:
yarn add react-native-navigation-modeUsing npm:
npm install react-native-navigation-modeNote: Auto-linking should handle setup automatically for all newer RN versions.
Expo (Managed Workflow)
npx expo install react-native-navigation-modeExpo Configuration
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 prebuildDevelopment Builds
Since this library contains native code, it requires a custom development build. You cannot use it with standard Expo Go.
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+)
Basic Usage
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>
);
}🔧 API Reference
React Hook (Recommended)
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.
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>
);
}Functions
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'isGestureNavigation(): Promise<boolean>
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/falsegetNavigationBarHeight(): Promise<number>
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)Types
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 |
Interaction Mode Values
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
Adaptive UI Layout
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>
);
}Conditional Rendering
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>
);
}Manual Detection
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:
❌ Common Bad Approaches
- 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's Solution
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
🚀 Critical for Edge-to-Edge Mode
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
Real-World Impact
// Before: Unreliable dimension-based guessing
const isGesture = screenHeight === windowHeight; // 😢 Breaks easily
// After: Direct system detection
const isGesture = await isGestureNavigation(); // 🎯 Always accuratePerfect 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
Platform Support
| 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+ |
Android Compatibility
- 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
How It Works
The library uses multiple detection methods for maximum accuracy:
config_navBarInteractionMode- Official Android configuration (API 29+)- Settings Provider - Checks
navigation_modesystem setting - WindowInsets API - Accurate navigation bar height detection (API 30+)
- Resource-based fallback - Navigation bar height for older devices
Performance Notes
- 🍎 iOS Behavior - iOS always returns
isGestureNavigation: trueandnavigationBarHeight: 0since 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
🐛 Troubleshooting
Common Issues
"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
🤝 Contributing
See the contributing guide to learn how to contribute to the repository and the development workflow.
📄 License
MIT
💖 Support the Project
❤️ Thanks to
- Module built using create-react-native-library
- Readme is edited using Typora
