Skip to content

JairajJangle/react-native-navigation-mode

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

61 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

react-native-navigation-mode

🧭 Detect Android navigation mode (3-button, 2-button, or gesture navigation) with native precision using Turbo modules.

npm version License Workflow Status Android iOS GitHub issues TS Turbo Module Expo npm bundle size

Visibility Sensor demo Visibility Sensor demo
3-Button Navigation
Traditional Android navigation
2-Button Navigation
Home + Back buttons
Gesture Navigation
Swipe-based navigation

✨ 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-mode

Using npm:

npm install react-native-navigation-mode

Note: Auto-linking should handle setup automatically for all newer RN versions.

Expo (Managed Workflow)

npx expo install react-native-navigation-mode
Expo 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 prebuild
Development 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/false

getNavigationBarHeight(): 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 or undefined Navigation bar height in density-independent pixels (dp).

Interaction Mode Values

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 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

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:

  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

Performance Notes

  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

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

LiberPay_Donation_Button Β Β Β Β Β Β Β Β Β  UPI_Donation_Button Β Β Β Β Β Β Β Β Β  Paypal_Donation_Button

❀️ Thanks to