Skip to content

baeckerherz/expo-mapbox-navigation

Repository files navigation

@baeckerherz/expo-mapbox-navigation

Alpha MIT License

Expo module for turn-by-turn navigation on iOS and Android using Mapbox Navigation SDK v3 (iOS) / Android. Single MapboxNavigation component, Expo config plugin for credentials, no vendored binaries. Minimal alternative to existing community wrappers.

Alpha — iOS is working in our builds; Android is not yet working (dependency resolution / Mapbox Maven). We need help to get Android over the line. APIs may change. Contributions welcome. Open an issue or reach out.

Package summary: Expo config plugin + native module; Mapbox Navigation SDK v3; iOS (SPM) and Android (Maven, drop-in NavigationView); turn-by-turn driving/walking/cycling; requires Expo ≥51, React Native ≥0.74, Mapbox public + secret tokens; alpha stage; TypeScript API via MapboxNavigation component.

Table of contents

Prerequisites

Mapbox requires two tokens (create both in your Mapbox tokens page):

Token Prefix Purpose
Public (access) token pk.xxx Used by the app at runtime: map tiles, Directions API, voice, etc. Set as mapboxAccessToken in the plugin.
Secret (downloads) token sk.xxx Used only at build time: Gradle (Android) and SPM (iOS) use it to download the Navigation SDK from Mapbox. Must have Downloads:Read scope. Not used by the app at runtime.

The same secret token is used for both platforms. For EAS Build, one EAS secret (e.g. MAPBOX_DOWNLOADS_TOKEN or MAPBOX_SECRET_TOKEN) can back both: iOS needs it in ~/.netrc for SPM; Android needs it in gradle.properties (the plugin writes it from mapboxSecretToken) and, when using centralized repo resolution, as env var MAPBOX_DOWNLOADS_TOKEN.

  • iOS: Add the secret token to ~/.netrc so SPM can download the SDK (local dev and EAS Build):

    machine api.mapbox.com
      login mapbox
      password YOUR_SECRET_TOKEN
    
  • Android: The plugin writes mapboxSecretToken to android/gradle.properties as MAPBOX_DOWNLOADS_TOKEN so Maven can download the SDK. For EAS Build, also set MAPBOX_DOWNLOADS_TOKEN as an EAS secret so the build can authenticate.

  • Android (local builds): Gradle must run on JVM 17 or greater. If you see “Executing Gradle on JVM versions 16 and lower has been deprecated”, set JAVA_HOME to a JDK 17+ installation (e.g. export JAVA_HOME=$(/usr/libexec/java_home -v 17) on macOS).

Installation

npx expo install @baeckerherz/expo-mapbox-navigation

Add the plugin in app.config.ts (or app.json). Prefer environment variables for tokens so you don’t commit secrets:

plugins: [
  ["@baeckerherz/expo-mapbox-navigation/plugin", {
    mapboxAccessToken: process.env.MAPBOX_ACCESS_TOKEN,
    mapboxSecretToken: process.env.MAPBOX_SECRET_TOKEN,
    navigationSdkVersion: "3.5.0",
  }],
]

Rebuild native projects:

npx expo prebuild --clean
npx expo run:ios
npx expo run:android

Usage

import { MapboxNavigation } from '@baeckerherz/expo-mapbox-navigation';

<MapboxNavigation
  coordinates={[
    { latitude: 47.2692, longitude: 11.4041 },
    { latitude: 48.2082, longitude: 16.3738 },
  ]}
  locale="de"
  onRouteProgressChanged={(e) => {
    console.log(e.nativeEvent.distanceRemaining);
  }}
  onCancelNavigation={() => navigation.goBack()}
  onFinalDestinationArrival={() => console.log('Arrived!')}
  style={{ flex: 1 }}
/>

API

Props

Route

Prop Type Description
coordinates Array<{ latitude, longitude }> Route waypoints (min 2). First = origin, last = destination.
waypointIndices number[] Indices in coordinates that are full waypoints (with arrival notification). Others are via-points. Must include first and last.
routeProfile string Routing profile. iOS: "mapbox/driving-traffic" (default), "mapbox/driving", "mapbox/walking", "mapbox/cycling". Android: omit "mapbox/" prefix.

Localization

Prop Type Description
locale string BCP 47 language for voice and UI (e.g. "de", "en-US"). Default: device locale.
mute boolean Mute voice guidance. Default: false.

Appearance

Prop Type Description
mapStyle string Mapbox style URL. Example: "mapbox://styles/mapbox/navigation-night-v1".
themeMode "day" | "night" | "auto" Day (default), night, or auto by time.
accentColor string Primary accent (hex). Example: "#007AFF".
routeColor string Route line color (hex). Overrides accentColor for the line.
bannerBackgroundColor string Instruction banner background (hex).
bannerTextColor string Instruction banner text (hex).

Events

Event Payload Description
onRouteProgressChanged { distanceRemaining, durationRemaining, distanceTraveled, fractionTraveled } Progress along the route.
onCancelNavigation User cancelled.
onWaypointArrival { waypointIndex } Reached an intermediate waypoint.
onFinalDestinationArrival Reached final destination.
onRouteChanged Route recalculated (reroute).
onUserOffRoute User left the route.
onError { message } Navigation error.

Architecture

iOS — SPM via config plugin
Mapbox Navigation SDK v3 is SPM-only. The Expo config plugin injects SPM package references into the Xcode project at prebuild. Version bumps are a single string change; no vendored xcframeworks.

Android — Drop-in NavigationView
We wrap the Nav SDK v3 NavigationView directly instead of rebuilding the UI.

Both — Expo Module API
expo-modules-core for native bridging: Fabric/New Architecture, type-safe props and events, and compatibility with Expo and bare React Native.

Why this exists

Existing wrappers have major drawbacks:

  • @badatgil/expo-mapbox-navigation: Vendored .xcframework on iOS (fragile; manual rebuild per SDK update). Android uses ~30 custom Kotlin components (~1100 LOC) instead of Mapbox’s drop-in view.
  • @homee/react-native-mapbox-navigation: Unmaintained (no activity since 2022), Nav SDK v2.1.1, no Expo, crashes on Android 13+.

Comparison

This module @badatgil/expo-mapbox-navigation @homee/react-native-mapbox-navigation
iOS SPM via config plugin Vendored .xcframeworks CocoaPods, Nav SDK v2
Android Drop-in NavigationView Custom UI (~1100 LOC) Custom UI (~500 LOC)
Nav SDK v3 v3 v2 (legacy)
Expo Module API Yes (Fabric-ready) Yes No
Multi-waypoint Yes Yes No
Maintenance Active Semi-active Abandoned

Status

Alpha. Platform status:

Platform Status Notes
iOS Working (Alpha) SPM via config plugin; tested in our project builds.
Android Not yet working Mapbox Maven / dependency resolution issues; help wanted to fix.

Goals: reliable SPM injection (iOS), drop-in NavigationView/NavigationViewController, event bridging. We want more feedback and testing. For prerelease we may publish under the alpha npm tag.

Help wanted — especially to get Android builds working (Gradle, Mapbox repo auth, or switching to ui-components if the drop-in artifact is unavailable).

Known risks

  • Android: Build and dependency resolution need community input.
  • Licensing: Mapbox Navigation SDK requires a commercial Mapbox license; this wrapper does not change that.

Integration testing

Use this flow to validate your changes before releasing: run the full integration from the repo root. It builds the example app with the local package, runs release checks, builds Android and iOS (with JVM 17+ for Gradle), and optionally runs Maestro E2E.

# Set Mapbox tokens so prebuild can fetch the SDK
export MAPBOX_PUBLIC_TOKEN="pk.xxx"
export MAPBOX_SECRET_TOKEN="sk.xxx"   # or MAPBOX_DOWNLOADS_TOKEN for Android

yarn test:integration              # build both Android and iOS, then E2E
yarn test:integration --ios       # build only iOS, then E2E
yarn test:integration --android   # build only Android, then E2E
yarn test:integration --e2e-only  # skip build; run only Maestro E2E (app must already be on simulator)

The integration script:

  1. Runs release checks (plugin build + static native checks for single provider / route clear).
  2. Links the local package in example/ and installs dependencies.
  3. Prebuilds the example app (generates ios/ and android/).
  4. Builds Android (./gradlew assembleDebug).
  5. Builds iOS (xcodebuild for Simulator).
  6. If Maestro is available, boots an iOS simulator (if none is running), installs/launches the example app, then runs the E2E flow (start navigation → tap “Next stop” to test route refresh).

The script installs Maestro and Java 17 if missing. For E2E it starts the simulator and the app automatically. The example app includes a “Next stop” button that changes the route coordinates; the Maestro flow verifies this doesn’t crash (single provider on iOS, clear+callback on Android). To run only E2E (app must already be installed): yarn test:integration --e2e-only.

Contributing

We welcome contributors and maintainers. If you work on Expo native modules, Mapbox SDKs, or React Native tooling, we’d love your help. If you or your company use this package, we’d love to hear from you (issues, discussions, or partner@baeckerherz.at) — it helps us prioritize and justify ongoing work.

Project layout: src/ (TypeScript API), ios/ (Swift + podspec), android/ (Kotlin + build.gradle), plugin/ (Expo config plugins), example/ (test app).

Run the example:

git clone https://github.com/baeckerherz/expo-mapbox-navigation.git
cd expo-mapbox-navigation && yarn install
cd example && yarn install
npx expo prebuild --clean
npx expo run:ios --device   # or: npx expo run:android

The example navigates from your location to Innsbruck Hauptbahnhof with German voice guidance.

Open an issue or submit a PR to get started.

License

MIT

Who uses this

We’d like to list teams and projects using this package (with your permission). If you’re using it, open an issue or email partner@baeckerherz.at.

Sponsors

Bäckerherz Bäckerherz — Founding sponsor. They build and use this module; the project exists thanks to their investment in open-source Expo tooling.

TourenFlow TourenFlow — Intelligent tour planning and route optimization.

To support the project or work with us: partner@baeckerherz.at.


About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors