npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-live-tracking-map

v1.0.0

Published

Drop-in live tracking map for React Native — customer side (watch a moving partner) and driver side (device GPS, compass heading, follow camera, throttled location reporting, arrival + off-route detection) in one component. Includes automatic Android Goog

Readme

react-native-live-tracking-map

A single, drop-in React Native component for live location tracking screens (delivery, cab, courier, field service, fleet — anything with a moving point and a destination). Built on top of react-native-maps.

Covers both sides of the same job:

  • Customer side — watch someone else move. You feed coordinates in from your socket/poll; the marker glides, the route draws, the ETA updates.
  • Driver side (mode="driver") — this device is the moving point. The package reads the GPS and compass, throttles the position reports back to your API, follows the camera navigation-style, detects arrival and going off-route, and hands off to Google/Apple Maps for voice navigation.

Features

  • Marker glides smoothly between location updates instead of snapping
  • Marker rotates to face its direction of travel (bearing)
  • Camera auto-fit / smooth animateCamera easing between the two points
  • Real, road-following route via a pluggable routing provider: Google Directions, Mapbox Directions, OSRM (free, no key), or your own backend
  • Native live traffic layer, plus traffic-aware ETA from Google/Mapbox
  • Optional built-in ETA/distance badge
  • Fully offline-safe default: with no routing provider configured, it draws a curved line with zero network calls
  • Imperative controls via ref: fitToMarkers, animateToPartner, animateToUser, refreshRoute

Driver side (mode="driver"):

  • Follow camera — course-up (map rotates to travel direction, tilted) or center, pausing when the driver pans the map and auto-resuming
  • Compass heading — marker faces the real heading even while stopped, with shortest-path rotation (no spinning the long way round through north)
  • useDriverTracking hook — fast first fix, high-accuracy watcher, accuracy filtering, and API reports throttled to "moved 20 m OR 15 s passed"
  • Arrival detection — onArrive fires once inside arrivalRadiusMeters, and re-arms when the destination changes (pickup → drop)
  • Off-route detection — measured against the drawn route, triggers an automatic reroute and onOffRoute
  • Waypoints — thread the route through intermediate stops (multi-drop)
  • Turn-by-turn handoff — openExternalNavigation() launches Google Maps / Apple Maps with the destination (and stops) preloaded

This does not include turn-by-turn voice navigation — that requires a provider-locked native navigation SDK. This covers live position tracking, real route, and traffic-aware ETA.

Install

npm install react-native-live-tracking-map
# or
yarn add react-native-live-tracking-map

react-native-maps is a peer dependency — React Native's CLI autolinking only scans the direct dependencies in your app's own package.json, so a native module has to live there to be discovered, no matter how any wrapper package declares it. This package's postinstall script handles that part for you automatically: it adds react-native-maps straight into your app's package.json and installs it, so you don't have to run that step by hand.

What can't be automated (true for any native React Native module, not specific to this package) is the native link + rebuild:

cd ios && pod install && cd ..
npx react-native run-ios      # or run-android

A JS reload / Fast Refresh will never pick up a native module — it requires an actual rebuild of the native app.

Driver side — optional extra modules

The driver features read the device's own sensors, so they need a geolocation module (and, optionally, a compass). These are optional peer dependencies, loaded lazily — a customer-only app never has to install them:

yarn add @react-native-community/geolocation react-native-compass-heading
# react-native-geolocation-service works too; it's picked up automatically

If neither geolocation module is present, useDriverTracking reports a no-module error instead of throwing, and the map still renders whatever coordinates you pass it by hand.

Make sure react-native-maps itself is configured for your platform. iOS's default map (PROVIDER_DEFAULT, Apple Maps) needs no API key at all — the Android side is what usually means editing AndroidManifest.xml and strings.xml by hand. This package automates that part; see below.

Android Google Maps API key — no manual XML editing

Why this can't just be a component prop: Android reads its Maps key from AndroidManifest.xml at native app startup, before any JS runs. A prop on <LiveTrackingMap apiKey="..."> physically can't reach that — that prop only feeds the Directions/Mapbox HTTP calls (JS, so it can be a prop, and already is). What this package gives you instead is the closest thing to "pass it in and go": one key, no hand-editing either XML file.

Pick whichever fits your workflow — all three end up writing the same two files (android/app/src/main/res/values/strings.xml and AndroidManifest.xml), and running any of them again (e.g. to rotate the key) updates the existing entries rather than duplicating them:

# a) One-off / CI: run it whenever you like
npx react-native-live-tracking-map-setup --key=YOUR_GOOGLE_MAPS_KEY
// b) Zero manual steps at all — add this to your APP's package.json,
//    then `yarn install` / `npm install` does it automatically:
{
  "reactNativeLiveTrackingMap": {
    "androidApiKey": "YOUR_GOOGLE_MAPS_KEY"
  }
}
# c) Or export an env var before installing — also picked up automatically:
export GOOGLE_MAPS_API_KEY=YOUR_GOOGLE_MAPS_KEY
yarn add react-native-live-tracking-map

If none of these are set, postinstall just prints the one command instead of two pages of manual XML steps. Either way you still need a real rebuild afterwards (npx react-native run-android) — a JS reload never picks up a manifest change, same as any other native config.

Keep the key restricted (package name + SHA-1 fingerprint) on the Google Cloud Console side — same as any Android app shipping a client key, secret or not; see the Security note below.

API keys — routing (Directions/Mapbox), passed as a prop

For routingProvider="google" or "mapbox", don't hardcode the key in your component tree. Use whatever secrets-management your app already has, keyed under the conventional names most tooling expects:

  • Google: GOOGLE_MAPS_API_KEY
  • Mapbox: MAPBOX_ACCESS_TOKEN

e.g. with react-native-config or Expo's app.config.js → extra field, then pass the resolved value into the apiKey prop:

import Config from 'react-native-config';

<LiveTrackingMap
  routingProvider="google"
  apiKey={Config.GOOGLE_MAPS_API_KEY}
  ...
/>

This keeps the component itself provider-agnostic — it never reads environment variables directly, it just takes whatever key you resolve and pass in.

If you're using Google for routing, an unrestricted-enough Google Maps key can serve both roles (map tiles via the Android setup above, Directions via this apiKey prop) — they're separate mechanisms reading the same key, not two keys you have to keep in sync.

Quick start (zero network calls)

import LiveTrackingMap from 'react-native-live-tracking-map';

<View style={{ flex: 1 }}>
  <LiveTrackingMap
    userLocation={userLocation}
    partnerLocation={partnerLocation}
  />
</View>

With real roads + traffic

// Google Directions (needs Directions API enabled + billing key)
<LiveTrackingMap
  userLocation={userLocation}
  partnerLocation={partnerLocation}
  routingProvider="google"
  apiKey={GOOGLE_MAPS_API_KEY}
  showTraffic
  showRouteInfoBadge
  onRouteInfo={(info) => console.log(info.durationInTrafficSeconds)}
/>

// Mapbox Directions (traffic-aware "driving-traffic" profile)
<LiveTrackingMap
  userLocation={userLocation}
  partnerLocation={partnerLocation}
  routingProvider="mapbox"
  apiKey={MAPBOX_ACCESS_TOKEN}
  showRouteInfoBadge
/>

// OSRM — free, no API key, no traffic data (good dev/default fallback)
<LiveTrackingMap
  userLocation={userLocation}
  partnerLocation={partnerLocation}
  routingProvider="osrm"
/>

// Bring your own routing backend
<LiveTrackingMap
  userLocation={userLocation}
  partnerLocation={partnerLocation}
  routingProvider="custom"
  customRouteFetcher={async (origin, destination, signal) => {
    const res = await fetch('https://my-api.com/route', { signal });
    const json = await res.json();
    return { coordinates: json.points, distanceMeters: json.distance, durationSeconds: json.duration };
  }}
/>

Driver side

The fastest version — DriverTrackingMap wires the GPS/compass hook into the map for you:

import { DriverTrackingMap } from 'react-native-live-tracking-map';

<DriverTrackingMap
  // Where the driver is headed right now — swap it on status change
  destination={isPickedUp ? customerCoord : restaurantCoord}
  // Already throttled: fires on 20m of movement OR every 15s
  onReportLocation={coords =>
    updateCurrentLocation({ latitude: coords.latitude, longitude: coords.longitude })
  }
  onArrive={() => setCanUpdateStatus(true)}
  partnerMarkerImage={DELIVERY_BOY}
  userMarkerImage={PIN_LOCATION}
  routingProvider="google"
  apiKey={Config.GOOGLE_MAPS_API_KEY}
  showRouteInfoBadge
  showTraffic
/>

If you already own the location plumbing (a Redux watcher, a foreground service, Expo Location), skip the wrapper and drive the map directly:

<LiveTrackingMap
  mode="driver"
  partnerLocation={myLocation}   // this device
  userLocation={destination}
  headingDegrees={compassHeading}
  followMode="course-up"
  onArrive={() => {}}
/>

mode="driver" only changes defaults (followMode: 'course-up', native flat-marker rotation, a visible navigate button). Every one of them is still individually overridable, so a hybrid screen is a matter of props.

Using the tracking hook on its own

const { location, heading, isTracking, error, reportNow, stop } = useDriverTracking({
  minReportDistanceMeters: 20,
  minReportIntervalMs: 15000,
  accuracyFilterMeters: 80,
  onReport: coords => api.updateCurrentLocation(coords),
  onError: e => e.code === 'permission-denied' && promptForSettings(),
});

// Force a report at a moment that matters, regardless of throttling
await reportNow();

Multi-stop / waypoints

<LiveTrackingMap
  mode="driver"
  partnerLocation={driver}
  userLocation={finalDrop}
  waypoints={[{ coordinate: firstDrop, title: 'Drop 1' }]}
  routingProvider="google"
  apiKey={KEY}
/>

Turn-by-turn handoff

Real voice navigation needs a provider-locked native SDK, which this package deliberately doesn't wrap. What it gives you instead is a one-call handoff:

import { openExternalNavigation } from 'react-native-live-tracking-map';

await openExternalNavigation(destination, { travelMode: 'driving' });
// or via the ref: mapRef.current?.openExternalNavigation()

Apple Maps on iOS, Google Maps turn-by-turn on Android, web URL as fallback.

Imperative controls

import { useRef } from 'react';
import LiveTrackingMap, { LiveTrackingMapHandle } from 'react-native-live-tracking-map';

const mapRef = useRef<LiveTrackingMapHandle>(null);

<LiveTrackingMap ref={mapRef} userLocation={userLocation} partnerLocation={partnerLocation} />

mapRef.current?.fitToMarkers();
mapRef.current?.refreshRoute();

// driver side
mapRef.current?.startFollow();
mapRef.current?.stopFollow();
mapRef.current?.isFollowing();
mapRef.current?.getDistanceToDestination();   // metres, straight line
await mapRef.current?.openExternalNavigation();

Props

| Prop | Type | Default | Notes | |---|---|---|---| | userLocation | LatLng \| null | — | Destination point | | partnerLocation | LatLng \| null | — | Moving point; update on every poll/socket tick | | userMarkerImage / partnerMarkerImage | ImageSourcePropType | — | Custom marker icons | | etaLabel | string | — | Shown under marker titles | | primaryColor | string | #FF6B35 | Route/polyline color | | animateMarkerDurationMs | number | 4500 | Match your update interval | | rotateMarkerToBearing | boolean | true | Rotate partner marker to face travel direction | | routingProvider | 'google' \| 'mapbox' \| 'osrm' \| 'custom' \| 'none' | 'none' | Real routing backend | | apiKey | string | — | Required for google / mapbox | | travelMode | 'driving' \| 'walking' \| 'bicycling' \| 'cycling' | 'driving' | | | showTraffic | boolean | true | Native traffic layer + traffic-aware duration where supported | | routeRefreshIntervalMs | number | 30000 | Throttle for re-fetching the route | | minRerouteDistanceMeters | number | 40 | Skip re-fetch if partner hasn't moved this far | | customRouteFetcher | (origin, destination, signal?) => Promise<RouteResult> | — | Required when routingProvider="custom" | | onRouteInfo / onRouteError | callbacks | — | | | showRouteInfoBadge | boolean | false | Built-in ETA/distance pill | | routeInfoFormatter | (info: RouteResult) => string | — | Customize the badge text |

Driver-side props

| Prop | Type | Default | Notes | |---|---|---|---| | mode | 'customer' \| 'driver' | 'customer' | Changes defaults only | | headingDegrees | number \| null | — | Compass/GPS heading; overrides derived bearing | | followMode | 'none' \| 'center' \| 'course-up' | 'none' / 'course-up' in driver mode | Camera behaviour | | followZoom / followPitch | number | 17 / 45 in course-up | Camera framing | | autoResumeFollowMs | number | 8000 | Resume following after the user pans; 0 = manual only | | onFollowSuspended / onFollowResumed | callbacks | — | | | waypoints | Waypoint[] | — | Intermediate stops, routed through and marked | | arrivalRadiusMeters | number | 60 | 0 disables arrival detection | | onArrive | (destination: LatLng) => void | — | Fires once per destination | | onDistanceToDestination | (meters: number) => void | — | Straight-line readout on every update | | offRouteThresholdMeters | number | 60 | Distance from the drawn route before rerouting; 0 disables | | onOffRoute | (meters: number) => void | — | | | showsUserLocation | boolean | false | Native blue dot | | mapPadding | {top,right,bottom,left} | — | For bottom sheets covering the map | | customMapStyle | any[] | — | Google Maps style JSON | | markerRotationMode | 'native' \| 'image' | 'native' in driver mode | Native flat-marker rotation vs image transform | | showExternalNavButton | boolean | true in driver mode | Google/Apple Maps handoff button | | mapViewProps | object | — | Escape hatch passed to MapView |

useDriverTracking(options)

| Option | Default | Notes | |---|---|---| | enabled | true | Pause when the screen isn't focused | | requestPermission | true | Android runtime permission on start | | distanceFilterMeters | 10 | OS-level update threshold | | accuracyFilterMeters | 80 | Drop fixes worse than this; 0 disables | | minReportDistanceMeters | 20 | Report after this much movement... | | minReportIntervalMs | 15000 | ...or at least this often | | onReport / onPosition / onError | — | Report is throttled, position is every fix | | enableCompass | true | Uses react-native-compass-heading if installed | | geolocationModule | — | Inject your own (Expo Location wrapper, tests) |

Returns { location, position, heading, speed, isTracking, error, start, stop, reportNow }.

Background tracking

This package tracks in the foreground only. Keeping a driver's location flowing while the app is backgrounded or the screen is off needs a foreground service (Android) and background location entitlements (iOS) — platform configuration that belongs in your app, not in a portable component. Wire your background service's coordinates into partnerLocation and everything else here keeps working.

Security note

Keep Google/Mapbox API keys restricted (HTTP referrer / bundle ID / IP restrictions) rather than shipping an unrestricted key in your app bundle, same as any client calling a paid maps API directly.

License

MIT