Facebook iconReact Native Mapbox Navigation Implementation Guide 2025
F22 logo
Blogs/Technology

Reviving react-native-mapbox-navigation

Written by Sarafathulla S
Feb 16, 2026
9 Min Read
Reviving react-native-mapbox-navigation Hero

Before diving into the updates made to react-native-mapbox-navigation, it’s important to acknowledge a practical gap many React Native developers run into. Mapbox does not provide official React Native bindings for its Navigation SDK, which often leaves teams navigating compatibility issues on their own. I’m writing this to document how that gap can be addressed in a way that keeps both Android and iOS builds stable, maintainable, and aligned with modern tooling.

React Native mapbox navigation

Android: From Gradle to Kotlin

The Android setup required modernization, as the existing android/build.gradle configuration reflected an older React Native ecosystem. Updating this layer was necessary to ensure compatibility with current Android build tools and avoid downstream dependency conflicts. Here's what changed:

  • Gradle was upgraded from version 4.2.2 to 7.2.2 to align with current Android tooling requirements and ensure long-term build stability. This move ensures compatibility with the latest Android build tools and dependencies.
classpath "com.android.tools.build:gradle:7.2.2"
  • Kotlin: Kotlin version management was restructured to resolve versions dynamically. The build first checks for a root-level kotlinVersion property and falls back to a module-specific value when absent. This approach centralizes SDK configuration while preserving flexibility across different project setups. to determine the appropriate Kotlin version to use.
  • Initially, it attempts to locate a property named kotlinVersion within the ext extension of the root project using rootProject.ext.has("kotlinVersion").
  • If such a property is found, its value is utilized by calling rootProject.ext.get("kotlinVersion").
  • However, in cases where the kotlinVersion property is not present in the root project's ext extension, the configuration defaults to using a version specified within the project’s own properties.
  • This fallback version is identified by the key "MapboxNavigation_kotlinVersion".
  • To facilitate this setup, the necessary version information, including the Kotlin version, has been defined within the gradle.properties file of the project.
  • The specified Kotlin version is 1.6.0, denoted by the entry MapboxNavigation_kotlinVersion=1.6.0.
  • Alongside the Kotlin version, other critical build configurations such as the compile SDK version (MapboxNavigation_compileSdkVersion=33), minimum SDK version (MapboxNavigation_minSdkVersion=21), and target SDK version (MapboxNavigation_targetSdkVersion=33) are also declared within the same gradle.properties file.
  • This approach ensures that the Kotlin version, along with other SDK versions, can be managed centrally and dynamically, providing flexibility and ease of updates across the project.

gradle.properties

MapboxNavigation_kotlinVersion=1.6.0
MapboxNavigation_compileSdkVersion=33
MapboxNavigation_minSdkVersion=21
MapboxNavigation_targetSdkVersion=33
  • It first checks if a property named kotlinVersion exists in the ext extension of the root project (rootProject.ext.has("kotlinVersion")). If it does, it uses this version (rootProject.ext.get("kotlinVersion")). If not, it falls back to a property defined within the project’s properties, specifically "MapboxNavigation_kotlinVersion".

Gradle Wrapper Version Update

We've updated our project's Gradle Wrapper to version 8.2. The gradle-wrapper.properties file now specifies:

distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip

Adding Waypoints: A Kotlin Challenge

Waypoint support was added to enable multi-stop navigation routes, allowing intermediate coordinates to be injected between origin and destination. This change improves routing flexibility while keeping the navigation flow consistent with Mapbox’s native expectations. This feature allows users to insert intermediate stops within their route. To accommodate this new capability, several adjustments were made to the MapboxNavigationManager.kt file:

  • We introduced a waypoints property, which is capable of receiving an array of coordinates.
  • These waypoints are represented as Point objects, each defined by latitude and longitude values.
  • A dynamic list of waypoints is generated based on user input, including the origin, destination, and any chosen intermediate stops.
  • Utilizing these waypoints, the route is crafted to support a more flexible and tailored navigation experience.

MapboxNavigationManager.kt:

/**
 * Sets waypoints for the MapboxNavigationView based on a React prop.
 * This function converts a ReadableArray of waypoint coordinates into a list of Point objects,
 * then updates the MapboxNavigationView with these waypoints.
 * 
 * @param view The MapboxNavigationView instance on which waypoints are to be set.
 * @param waypointsArray The array of waypoints received from React, where each waypoint
 * is expected to be an array of two numbers: [longitude, latitude].
 */
@ReactProp(name = "waypoints")
fun setWaypoints(view: MapboxNavigationView, waypointsArray: ReadableArray?) {
    // Check if the waypointsArray is not null to proceed
    waypointsArray?.let {
        // Initialize a mutable list to hold the converted Point objects
        val waypoints = mutableListOf<Point>()
        // Iterate over each item in the waypointsArray
        for (i in 0 until it.size()) {
            // Attempt to get the array (longitude, latitude) for the current waypoint
            val waypointArray = it.getArray(i)
            // Check if the waypointArray is not null and contains at least two elements
            if (waypointArray !== null && waypointArray.size() >= 2) {
                // Extract longitude and latitude values
                val longitude = waypointArray.getDouble(0)
                val latitude = waypointArray.getDouble(1)
                // Create a Point object from the longitude and latitude and add it to the waypoints list
                waypoints.add(Point.fromLngLat(longitude, latitude))
            }
        }

        // Update the MapboxNavigationView with the list of waypoints
        view.setWaypoints(waypoints)
    }
}

MapboxNavigationView.kt:

// Defining a variable to hold waypoints
private var waypoints: List<Point>? = null

// Starts the routing process
private fun startRoute() {
    // Registering necessary event listeners for navigation updates
    mapboxNavigation.registerRoutesObserver(routesObserver)
    mapboxNavigation.registerArrivalObserver(arrivalObserver)
    mapboxNavigation.registerRouteProgressObserver(routeProgressObserver)
    mapboxNavigation.registerLocationObserver(locationObserver)
    mapboxNavigation.registerVoiceInstructionsObserver(voiceInstructionsObserver)
    mapboxNavigation.registerRouteProgressObserver(replayProgressObserver)

    // Creating a list of coordinates for the route that includes the origin, destination, and any waypoints
    val coordinatesList = mutableListOf<Point>()
    // Adding origin to the list if it's not null
    this.origin?.let { coordinatesList.add(it) }
    // Adding all waypoints to the list if they exist
    this.waypoints?.let { coordinatesList.addAll(it) }
    // Adding destination to the list if it's not null
    this.destination?.let { coordinatesList.add(it) }

    // Finding a route with the specified coordinates
    findRoute(coordinatesList)
}

// Function to find a route based on a list of coordinates
private fun findRoute(coordinates: List<Point>) {
    try {
        // Building route options with the desired parameters
        val routeOptionsBuilder = RouteOptions.builder()
            .applyDefaultNavigationOptions()
            .applyLanguageAndVoiceUnitOptions(context)
            .coordinatesList(coordinates) // Setting the list of coordinates (origin, waypoints, destination)
            .profile(DirectionsCriteria.PROFILE_DRIVING) // Setting the routing profile to driving
            .steps(true) // Including step-by-step instructions in the route

        // Optionally setting maximum height and width if provided
        maxHeight?.let { routeOptionsBuilder.maxHeight(it) }
        maxWidth?.let { routeOptionsBuilder.maxWidth(it) }

        // Building the final route options
        val routeOptions = routeOptionsBuilder.build()

        // Requesting routes with the specified options
        mapboxNavigation.requestRoutes(
            routeOptions,
            object : RouterCallback {
                override fun onRoutesReady(
                    routes: List<DirectionsRoute>,
                    routerOrigin: RouterOrigin
                ) {
                    // Handling successful route finding
                    setRouteAndStartNavigation(routes)
                }

                override fun onFailure(
                    reasons: List<RouterFailure>,
                    routeOptions: RouteOptions
                ) {
                    // Handling route finding failure
                    sendErrorToReact("Error finding route $reasons")
                }

                override fun onCanceled(routeOptions: RouteOptions, routerOrigin: RouterOrigin) {
                    // Handling route request cancellation (no implementation needed here)
                }
            }
        )
    } catch (ex: Exception) {
        // Handling any exceptions during route finding
        sendErrorToReact(ex.toString())
    }
}

Android NDK Compatibility: A Tricky Scenario

Let’s Build Your React Native App Together!

We build powerful React Native apps that run smoothly on iOS and Android — fast, reliable, and ready to scale.

One thing to note in Android is the compatibility challenge between React Native and the Mapbox Navigation SDK. At the time of this update, React Native version 0.72.6 uses NDK version 23, while the Mapbox Navigation SDK supports only up to NDK version 21. This introduces a version alignment challenge between React Native and the Mapbox Navigation SDK, where supported NDK versions do not currently overlap.

Developers are effectively limited to two viable paths, each with trade-offs related to stability and future upgrades.

  1. They can either downgrade React Native to version 0.70, which uses NDK version 21 and is compatible with the Mapbox Navigation SDK

(or)

  1. They can patiently wait for Mapbox to release a version that supports NDK version 23. It’s a testament to the ever-evolving nature of the tech world; sometimes, we need to juggle dependencies and versions to keep everything in sync.

iOS: Podspec and Swift Overhaul

On iOS, the update process was more straightforward due to fewer tooling conflicts and clearer dependency boundaries within the Mapbox Navigation SDK. The react-native-mapbox-navigation.podspec file was updated to meet current iOS platform requirements and ensure compatibility with newer Mapbox Navigation releases.

  • iOS Version: We bumped up the minimum iOS version from 11.0 to 12.4 to stay current with Apple's requirements.
  • Mapbox Navigation: The dependency on Mapbox Navigation was updated to version 2.17.0, ensuring compatibility with the latest features and improvements.

React-native-mapbox-navigation.podspec:

//11.0 -> 12.4
s.platforms = { :ios => "12.4" }

//2.1.1 -> 2.17.0
s.dependency "MapboxNavigation", "~> 2.17.0"

Swift Integration: Waypoints in the Mix

The iOS update also brought waypoint support to MapboxNavigationView.swift. Waypoint support on iOS followed a predictable integration path, mirroring Android’s coordinate handling to maintain behavioral consistency across platforms.

  • Similar to Android, we introduced a waypoints prop for iOS, allowing users to specify intermediate stops.
  • Waypoints are processed as an array of coordinates, just like on the Android side.
  • We dynamically create a list of waypoints and use them to define the navigation route, providing a consistent experience across both platforms.

ios/MapboxNavigationManager.m:

/**
 * Exports the `waypoints` property to React Native, allowing it to be set from the JavaScript side.
 * This property is expected to be an array of arrays, where each inner array represents a waypoint
 * consisting of two elements: longitude and latitude (in that order).
 * 
 * By defining this property, we enable the React Native app to dynamically specify waypoints for navigation
 * routes directly from JavaScript, enhancing the flexibility of route creation and modification.
 * 
 * Example JavaScript usage:
 * <MapboxNavigationView waypoints={[[longitude1, latitude1], [longitude2, latitude2]]} />
 */
RCT_EXPORT_VIEW_PROPERTY(waypoints, NSArray<NSArray>)

ios/MapboxNavigationManager.m:

// Property to store waypoints received from React Native. It triggers layout update on change.
@objc var waypoints: NSArray = [] {
    didSet { setNeedsLayout() }
}

// Embeds the navigation view into the current view, setting up the route with waypoints.
private func embed() {
    // Ensuring origin and destination coordinates are correctly set
    guard origin.count == 2 && destination.count == 2 else { return }
    
    embedding = true

    // Creating waypoints for origin and destination
    let originWaypoint = Waypoint(coordinate: CLLocationCoordinate2D(latitude: origin[1] as! CLLocationDegrees, longitude: origin[0] as! CLLocationDegrees))
    let destinationWaypoint = Waypoint(coordinate: CLLocationCoordinate2D(latitude: destination[1] as! CLLocationDegrees, longitude: destination[0] as! CLLocationDegrees))

    // Initializing the waypoints array with origin to start building the complete route
    var waypointsArray = [originWaypoint]
    
    // Looping through any intermediate waypoints provided and adding them to the waypoints array
    for waypointArray in waypoints {
        if let waypointCoordinates = waypointArray as? NSArray, waypointCoordinates.count == 2,
           let lat = waypointCoordinates[1] as? CLLocationDegrees, let lon = waypointCoordinates[0] as? CLLocationDegrees {
            let waypoint = Waypoint(coordinate: CLLocationCoordinate2D(latitude: lat, longitude: lon))
            waypointsArray.append(waypoint)
        }
    }
    
    // Appending destination waypoint to the array
    waypointsArray.append(destinationWaypoint)

    // Creating navigation options with the waypoints array, setting the profile to driving while avoiding traffic
    let options = NavigationRouteOptions(waypoints: waypointsArray, profileIdentifier: .automobileAvoidingTraffic)

    // Requesting route calculation with the given options
    Directions.shared.calculate(options) { [weak self] (_, result) in
        guard let strongSelf = self, let parentVC = strongSelf.parentViewController else {
            return
        }
        
        switch result {
            case .failure(let error):
                // Handling route calculation failure
                strongSelf.onError!(["message": error.localizedDescription])
            case .success(let response):
                // Proceeding with navigation setup upon successful route calculation
                guard let weakSelf = self else {
                    return
                }
                
                // Setting up the navigation service and navigation options
                let navigationService = MapboxNavigationService(routeResponse: response, routeIndex: 0, routeOptions: options, simulating: strongSelf.shouldSimulateRoute ? .always : .never)
                
                let navigationOptions = NavigationOptions(navigationService: navigationService)
                // Creating and configuring the NavigationViewController
                let vc = NavigationViewController(for: response, routeIndex: 0, routeOptions: options, navigationOptions: navigationOptions)

                vc.showsEndOfRouteFeedback = strongSelf.showsEndOfRouteFeedback
                StatusView.appearance().isHidden = strongSelf.hideStatusView

                NavigationSettings.shared.voiceMuted = strongSelf.mute;
                
                vc.delegate = strongSelf
            
                // Embedding the navigation view controller into the current view hierarchy
                parentVC.addChild(vc)
                strongSelf.addSubview(vc.view)
                vc.view.frame = strongSelf.bounds
                vc.didMove(toParent: parentVC)
                strongSelf.navViewController = vc
        }
        
        // Updating state flags after embedding
        strongSelf.embedding = false
        strongSelf.embedded = true
    }
}

src/typings.ts:

//add waypoints type
waypoints?: Coordinate[];

dist/typings.d.ts:

//add waypoints into IMapboxNavigationProps
waypoints?: Coordinate[];

How to Install My Forked Version

All the changes and enhancements I've made to react-native-mapbox-navigation are available in my forked repository on GitHub. If you want to use my version, follow these steps to install it:

Uninstall the Original Package (if already installed)

npm uninstall react-native-mapbox-navigation
# or
yarn remove react-native-mapbox-navigation

Install My Forked Version:

GitHub - sarafhbk/react-native-mapbox-navigation: A navigation UI ready to drop into your React Native application
A navigation UI ready to drop into your React Native application - sarafhbk/react-native-mapbox-navigation
npm install https://github.com/sarafhbk/react-native-mapbox-navigation
# or
yarn add https://github.com/sarafhbk/react-native-mapbox-navigation

The Challenge of Non-Native Mastery

Working across Gradle, Kotlin, Podspec, and Swift highlights a common challenge in React Native development: maintaining native-level changes without full-time specialization in either platform. Understanding Gradle, Kotlin, Podspec, and Swift was like deciphering a complex code. That's where ChatGPT came to the rescue, offering guidance and solutions when navigating these uncharted territories.

Let’s Build Your React Native App Together!

We build powerful React Native apps that run smoothly on iOS and Android — fast, reliable, and ready to scale.

Impact

As a result, react-native-mapbox-navigation is now aligned with modern Android and iOS tooling, supports waypoint-based navigation, and offers improved compatibility for production React Native applications. It's now updated with new tools, improved features, and the ability to include waypoints. This upgrade means developers can build better and more personalized navigation experiences in their React Native apps.

Frequently Asked Questions

Why should I use your forked version of react-native-mapbox-navigation?

 My forked version includes several enhancements and updates that improve the functionality and flexibility of react-native-mapbox-navigation. If you want access to the latest features and improvements, my version is a great choice.

What's the difference between your version and the original package?

My version includes updates like support for waypoints, compatibility enhancements, and bug fixes. You can refer to my repository's README or documentation for a detailed list of changes.

Do I need to make any additional configuration changes?

In most cases, the installation process should be straightforward. However, depending on your project's requirements, you may need to make specific configuration changes, which I'll guide in the README of my repository.

How can I report issues or contribute to your forked repository?

I encourage developers to contribute and report issues. You can visit the issues page of my GitHub repository to report problems or provide feedback. If you'd like to contribute, feel free to create pull requests.

Author-Sarafathulla S
Sarafathulla S

I'm a web and mobile developer with 5 years of experience in React and React Native, creating innovative solutions for complex applications and consistently delivering high-quality projects.

Share this article

Phone

Next for you

8 Best GraphQL Libraries for Node.js in 2025 Cover

Technology

Jan 29, 20268 min read

8 Best GraphQL Libraries for Node.js in 2025

Why do some GraphQL APIs respond in milliseconds while others take seconds? The difference often comes down to choosing the right GraphQL library for Node.js. According to npm trends, Apollo Server Express alone sees over 800,000 weekly downloads, proving that developers need reliable tools to build production-ready GraphQL servers. The truth is, building GraphQL APIs in Node.js has never been easier, but picking the wrong library can slow down your entire application. Modern web applications d

I Tested 9 React Native Animation Libraries (Here’s What Works) Cover

Technology

Feb 10, 202614 min read

I Tested 9 React Native Animation Libraries (Here’s What Works)

Why do some mobile apps feel smooth while others feel clunky? I’ve noticed the difference is usually animations under load, especially during scrolling, navigation, and gesture-heavy screens. Google research shows 53% of mobile site visits are abandoned if pages take longer than three seconds to load, and the same performance expectations carry over to mobile apps. The truth is, smooth animations in React Native apps are no longer a luxury; they’re a must-have for a modern, engaging user experi

9 Critical Practices for Secure Web Application Development Cover

Technology

Jan 29, 20267 min read

9 Critical Practices for Secure Web Application Development

In 2026, developing modern web applications requires a balance between speed and security. Product strategy often pressures development teams to move fast, and ignoring application security can cause catastrophic results. For example, post-credential-based attacks have caused over $5 billion in losses. Security vulnerabilities in web applications are not just technical security problems; they are a business risk. The truth is that security incidents happen when web developers think about web se