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

expo-realtime-maps-navigation

v1.0.5

Published

JavaScript-pure React Native navigation package with Google Places + HERE Routing APIs, dual maps support, and complete customization - No native modules required!

Readme

expo-realtime-maps-navigation

npm version License: MIT TypeScript JavaScript Pure

🚗 JavaScript-Pure React Native Navigation Package

Real-time navigation with Google Places + HERE Routing APIs, dual maps support (react-native-maps/expo-maps), and complete customization - No native modules required!

✨ Features

  • 🎯 JavaScript-Pure: No native modules, works with Expo Go and all React Native environments
  • 🗺️ HERE Routing API: Enterprise-grade routing and turn-by-turn guidance via REST API
  • 🔍 Google Places: Search, autocomplete, and geocoding with Google Places API
  • 📱 Dual Maps Support: Works with react-native-maps and expo-maps
  • 🔄 Background Location: Reliable tracking with Expo Location
  • 🎨 Modern React APIs: Hooks, Context, TypeScript-first development
  • ⚙️ Expo Compatible: Works with Expo Go, no config plugin needed
  • 🌍 Multi-modal: Car, scooter, bike, pedestrian routing
  • 🎵 Voice Simulation: Navigation guidance simulation for testing
  • 🔋 Lightweight: Pure JavaScript implementation, minimal bundle impact

🚀 Quick Start

1. Installation

npm install expo-realtime-maps-navigation react-native-maps expo-location

2. API Keys Setup

You'll need API keys from:

HERE Developer Portal (for routing):

Google Cloud Console (for places):

3. Initialize the Navigation Service

import { initializeNavigation } from 'expo-realtime-maps-navigation';

// Initialize with your API keys
await initializeNavigation({
  here: 'YOUR_HERE_API_KEY',
  google: 'YOUR_GOOGLE_API_KEY'
});

4. Basic Usage

import React, { useRef, useEffect, useState } from 'react';
import MapView, { Polyline } from 'react-native-maps';
import {
  initializeNavigation,
  computeRoute,
  startNavigation,
  useRouteProgress,
  getPolylineFromHereRoute,
  TRANSPORT_MODES,
  searchPlaces,
  getCurrentLocation,
} from 'expo-realtime-maps-navigation';

const App = () => {
  const mapRef = useRef<MapView>(null);
  const [route, setRoute] = useState(null);
  const progress = useRouteProgress();

  useEffect(() => {
    // Initialize the navigation service
    initializeNavigation({
      here: 'YOUR_HERE_API_KEY',
      google: 'YOUR_GOOGLE_API_KEY'
    });
  }, []);

  const startNavigationExample = async () => {
    // Get current location
    const currentLocation = await getCurrentLocation();
    
    // Search for a destination
    const places = await searchPlaces('restaurant near me', currentLocation.position);
    
    // Compute route to first result
    const route = await computeRoute({
      origin: currentLocation.position,
      destination: places[0].position,
      transportMode: TRANSPORT_MODES.CAR,
    });

    setRoute(route);

    // Start navigation with simulation
    await startNavigation(route, { 
      simulate: true, // Use simulation for testing
      speedKph: 60 
    });
  };

  return (
    <MapView ref={mapRef} style={{ flex: 1 }} showsUserLocation>
      {route && (
        <Polyline 
          coordinates={getPolylineFromHereRoute(route)} 
          strokeWidth={6} 
          strokeColor="#007AFF"
        />
      )}
    </MapView>
  );
};

📚 Documentation

Core API

Initialization

import { initializeNavigation } from 'expo-realtime-maps-navigation';

// Required: Initialize with your API keys
await initializeNavigation({
  here: 'YOUR_HERE_API_KEY',    // HERE REST API key
  google: 'YOUR_GOOGLE_API_KEY' // Google Places/Geocoding API key
});

Places Search

import { searchPlaces, autocompletePlaces } from 'expo-realtime-maps-navigation';

// Search for places
const places = await searchPlaces('restaurant', currentLocation, 5000); // 5km radius

// Autocomplete (as user types)
const suggestions = await autocompletePlaces('pizza', currentLocation);

// Get place details
const placeDetails = await getPlaceDetails(place.id);

Route Computation

import { computeRoute, TRANSPORT_MODES } from 'expo-realtime-maps-navigation';

const route = await computeRoute({
  origin: { latitude: 37.7749, longitude: -122.4194 },
  destination: { latitude: 37.7849, longitude: -122.4094 },
  transportMode: TRANSPORT_MODES.CAR,
  waypoints: [{ latitude: 37.7799, longitude: -122.4144 }], // Optional
  avoidances: ['tolls', 'highways'], // Optional
  routePreference: 'fastest', // 'fastest' | 'shortest' | 'balanced'
});

Navigation Control

import { startNavigation, stopNavigation } from 'expo-realtime-maps-navigation';

// Start navigation
await startNavigation(route, {
  simulate: true,  // Use simulation for testing
  speedKph: 60,    // Simulation speed
  voiceGuidance: false, // Voice simulation not implemented
});

// Stop navigation
await stopNavigation();

React Hooks

useNavigationSession

import { useNavigationSession } from 'expo-realtime-maps-navigation';

const MyComponent = () => {
  const navigation = useNavigationSession();

  return (
    <View>
      <Text>Status: {navigation.isActive ? 'Navigating' : 'Idle'}</Text>
      <Text>Current Route: {navigation.currentRoute?.distance}m</Text>
    </View>
  );
};

useRouteProgress

import { useRouteProgress } from 'expo-realtime-maps-navigation';

const ProgressDisplay = () => {
  const progress = useRouteProgress();

  if (!progress) return null;

  return (
    <View>
      <Text>Next: {progress.nextManeuver?.instruction}</Text>
      <Text>Distance: {progress.distanceToNextManeuver}m</Text>
      <Text>ETA: {progress.eta.toLocaleTimeString()}</Text>
      <Text>Speed: {Math.round(progress.speed * 3.6)} km/h</Text>
    </View>
  );
};

useLiveLocation

import { useLiveLocation } from 'expo-realtime-maps-navigation';

const LocationDisplay = () => {
  const location = useLiveLocation();

  return (
    <Text>
      Location: {location?.position.latitude.toFixed(6)},{location?.position.longitude.toFixed(6)}
      (Accuracy: ±{location?.accuracy}m)
      Source: {location?.source} {/* 'gps' or 'simulation' */}
    </Text>
  );
};

Map Integration Helpers

import {
  getPolylineFromHereRoute,
  followUserOnMap,
  fitRoute,
} from 'expo-realtime-maps-navigation';

// Convert HERE route to react-native-maps format
const coordinates = getPolylineFromHereRoute(route);

// Auto-follow user position during navigation
followUserOnMap(mapRef, currentPosition, {
  bearing: currentBearing,
  zoom: 16,
});

// Fit route to map view
fitRoute(mapRef, route, 50); // 50pt padding

Location Services

import { getCurrentLocation, requestLocationPermissions } from 'expo-realtime-maps-navigation';

// Request location permissions
const status = await requestLocationPermissions();

// Get current location
const location = await getCurrentLocation({
  enableHighAccuracy: true,
  timeout: 15000,
  maximumAge: 10000
});

// For background location, you can also request:
// await Location.requestBackgroundPermissionsAsync()

Navigation Events

import { events } from 'expo-realtime-maps-navigation';

// Listen to navigation events
events.on('navigationProgress', (progress) => {
  console.log('Route progress:', progress.routeProgress);
  console.log('Next maneuver:', progress.nextManeuver);
});

events.on('maneuverWarning', ({ maneuver, distance }) => {
  console.log(`In ${distance}m: ${maneuver.instruction}`);
});

events.on('navigationArrived', () => {
  console.log('Destination reached!');
});

Complete Example

import React, { useEffect, useState } from 'react';
import { View, Button, Text } from 'react-native';
import {
  initializeNavigation,
  searchPlaces,
  computeRoute,
  startNavigation,
  getCurrentLocation,
  events,
  TRANSPORT_MODES
} from 'expo-realtime-maps-navigation';

const NavigationExample = () => {
  const [isReady, setIsReady] = useState(false);
  const [progress, setProgress] = useState(null);

  useEffect(() => {
    // Initialize services
    initializeNavigation({
      here: 'YOUR_HERE_API_KEY',
      google: 'YOUR_GOOGLE_API_KEY'
    }).then(() => setIsReady(true));

    // Listen to events
    events.on('navigationProgress', setProgress);

    return () => events.removeAllListeners();
  }, []);

  const startNavigation = async () => {
    const location = await getCurrentLocation();
    const places = await searchPlaces('coffee shop', location.position);
    const route = await computeRoute({
      origin: location.position,
      destination: places[0].position,
      transportMode: TRANSPORT_MODES.CAR
    });
    await startNavigation(route, { simulate: true });
  };

  return (
    <View>
      <Text>Ready: {isReady ? 'Yes' : 'No'}</Text>
      {progress && (
        <Text>Progress: {Math.round(progress.routeProgress * 100)}%</Text>
      )}
      <Button title="Start Navigation" onPress={startNavigation} />
    </View>
  );
};

📋 Data Formats & API Reference

Route Object Format

interface Route {
  id: string;                    // "here_1755383063027"
  distance: number;              // Distance in meters (6940)
  duration: number;              // Duration in seconds (1380)
  polyline: string;              // HERE encoded polyline
  maneuvers: Maneuver[];         // Turn-by-turn instructions
  summary: {
    startAddress: string;        // "Départ" or actual address
    endAddress: string;          // "Arrivée" or actual address  
    routeType: string;          // "car", "scooter", etc.
  };
  sections: RouteSection[];      // Route segments (for waypoints)
}

interface Maneuver {
  id: string;                    // "maneuver_0"
  instruction: string;           // "Turn left onto Rue de Rivoli"
  distance: number;              // Distance to maneuver in meters
  duration: number;              // Time to maneuver in seconds
  type: string;                  // "start", "turn", "continue", "finish"
  position?: {
    latitude: number;
    longitude: number;
  };
  roadName: string;              // "Rue de Rivoli"
  direction: string;             // "left", "right", "straight"
  severity: string;              // "light", "quite", "info"
}

Place Object Format

interface Place {
  id: string;                    // Google place_id
  name: string;                  // "Le Ju'"
  address: string;               // "16 Rue des Archives, 75004 Paris, France"
  position: {
    latitude: number;            // 48.8576911
    longitude: number;           // 2.3548054
  };
  types?: string[];              // ["restaurant", "food", "establishment"]
  rating?: number;               // 4.2
  priceLevel?: number;           // 1-4 scale
  phoneNumber?: string;
  website?: string;
}

Location Object Format

interface Location {
  position: {
    latitude: number;            // 48.8566
    longitude: number;           // 2.3522
  };
  accuracy: number;              // Accuracy in meters (10)
  heading?: number;              // Bearing in degrees (0-360)
  speed?: number;               // Speed in m/s
  source: 'gps' | 'simulation'; // Location source
  timestamp: Date;               // When location was obtained
}

Navigation Progress Format

interface NavigationProgress {
  position: {
    latitude: number;
    longitude: number;
  };
  bearing: number;               // Current heading in degrees
  speed: number;                 // Speed in m/s
  speedKmh: number;             // Speed in km/h
  accuracy: number;              // GPS accuracy in meters
  nextManeuver?: Maneuver;       // Next turn/instruction
  distanceToNextManeuver: number; // Distance in meters
  distanceToDestination: number;  // Remaining distance in meters
  eta: Date;                     // Estimated time of arrival
  roadName?: string;             // Current road name
  speedLimit?: number;           // Posted speed limit
  instructionText: string;       // "Turn left in 200m"
  routeProgress: number;         // 0.0 to 1.0 (completion percentage)
  deviationMeters: number;       // Distance from route in meters
  inTunnel: boolean;            // GPS signal status
}

Constants & Enums

// Transport modes
export const TRANSPORT_MODES = {
  CAR: 'car',
  SCOOTER: 'scooter', 
  BIKE: 'bike',
  PEDESTRIAN: 'pedestrian'
};

// Route preferences  
export const ROUTE_PREFERENCES = {
  FASTEST: 'fastest',
  SHORTEST: 'shortest',
  BALANCED: 'balanced'
};

// Avoidances
export const AVOIDANCES = {
  TOLLS: 'tolls',
  HIGHWAYS: 'highways', 
  FERRIES: 'ferries'
};

Event Types

// Navigation events
events.on('navigationProgress', (progress: NavigationProgress) => {});
events.on('navigationStarted', ({ route: Route, options: any }) => {});
events.on('navigationStopped', () => {});
events.on('navigationPaused', () => {});
events.on('navigationResumed', () => {});
events.on('navigationArrived', () => {});

// Maneuver warnings  
events.on('maneuverWarning', ({ 
  maneuver: Maneuver, 
  distance: number,    // Distance to maneuver in meters
  level: 'far' | 'near' | 'immediate' // Warning urgency
}) => {});

// Error handling
events.on('error', (error: NavigationError) => {});
events.on('navigationError', ({ 
  type: string, 
  code: string, 
  message: string, 
  timestamp: Date 
}) => {});

Example Real API Responses

HERE Route Response (Paris to Tour Eiffel):

{
  "distance": 6940,
  "duration": 1380, 
  "maneuvers": 20,
  "polyline": "BGsrgm9CgsyvE4I7kBgFvRoB_E...",
  "summary": {
    "startAddress": "Départ",
    "endAddress": "Arrivée",
    "routeType": "car"
  }
}

Google Places Response (Restaurant search):

{
  "name": "Le Ju'",
  "address": "16 Rue des Archives, 75004 Paris, France",
  "position": {
    "latitude": 48.8576911,
    "longitude": 2.3548054
  },
  "rating": 4.2,
  "types": ["restaurant", "food"]
}

Utility Functions

// Package utilities
import { utils } from 'expo-realtime-maps-navigation';

utils.getSDKVersion();        // "1.0.0-beta (JavaScript Pure Edition)"
utils.isInitialized();        // true/false
await utils.clearCache();     // Clears routing and places cache

// Geometry helpers (available but not in main export)
import { 
  calculateDistance,           // Haversine distance between two points
  calculateBearing,           // Bearing between two points
  decodePolyline             // Decode HERE polyline to coordinates
} from 'expo-realtime-maps-navigation/lib/services/here/routing';

const distance = calculateDistance(
  { latitude: 48.8566, longitude: 2.3522 },
  { latitude: 48.8584, longitude: 2.2945 }
); // Returns distance in meters

Error Handling

import { NavigationError } from 'expo-realtime-maps-navigation';

try {
  await computeRoute(request);
} catch (error) {
  if (error instanceof NavigationError) {
    console.log('Error code:', error.code);
    console.log('Error message:', error.message);
    console.log('Original error:', error.originalError);
  }
}

// Common error codes:
// - INITIALIZATION_FAILED
// - ROUTE_COMPUTATION_FAILED  
// - NAVIGATION_START_FAILED
// - LOCATION_FAILED
// - PERMISSION_FAILED

🛠️ Key Differences from Native Versions

JavaScript-Pure Implementation

This package uses web APIs instead of native SDK modules:

  • HERE Routing API (REST) instead of HERE SDK
  • Google Places API (REST) instead of native SDKs
  • Expo Location instead of native location services
  • EventEmitter for navigation events instead of native callbacks

Benefits

  • ✅ Works with Expo Go
  • ✅ No native module compilation
  • ✅ Cross-platform compatibility
  • ✅ Easier deployment and updates
  • ✅ No platform-specific setup

Limitations

  • ❌ No offline voice guidance (simulation only)
  • ❌ Requires internet connection for routing
  • ❌ No native background location optimizations
  • ❌ Limited to REST API rate limits

Migration from Native Versions

// Before (native SDK)
<HereNavProvider platformKeys={...}>

// After (JavaScript-pure)
await initializeNavigation({ here: '...', google: '...' });

📱 Platform Requirements

  • React Native: 0.70+
  • Expo SDK: 48+ (including Expo Go)
  • Android: API 21+ (Android 5.0+)
  • iOS: 11.0+
  • Internet Connection: Required for routing and places

🔑 API Keys Setup

HERE Developer Portal

  1. Visit developer.here.com
  2. Create a new project
  3. Generate a REST API key
  4. Enable "Routing API v8"

Google Cloud Console

  1. Visit console.cloud.google.com
  2. Create a new project or select existing
  3. Enable "Places API" and "Geocoding API"
  4. Create an API key and restrict it to these APIs

🔒 Permissions

Required Dependencies

npm install expo-location

Android Permissions

Add to android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

iOS Permissions

Add to ios/YourApp/Info.plist:

<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs location access for navigation</string>

✅ Tested & Verified

This package has been thoroughly tested with:

  • HERE Routing API v8: Complete route calculation with real API responses
  • Google Places API: Search, autocomplete, and geocoding functionality
  • Expo Location: GPS permissions and position tracking
  • Complete Integration: End-to-end navigation workflows
  • Multi-platform: Node.js compatible for testing, React Native ready

All core features verified with real API keys and production-ready responses.

🐛 Troubleshooting

Common Issues

1. "API key invalid" errors

  • Verify your HERE API key has Routing API v8 enabled
  • Check Google API key has Places API and Geocoding API enabled
  • Ensure API keys are not restricted to wrong domains/IPs

2. "Permission denied" on location

import { requestLocationPermissions } from 'expo-realtime-maps-navigation';

const status = await requestLocationPermissions();
if (status !== 'granted') {
  // Handle permission denial
}

3. Navigation not starting

  • Check internet connection
  • Verify initializeNavigation() was called first
  • Ensure route computation was successful

4. Performance Issues

// Use simulation for testing to avoid GPS overhead
await startNavigation(route, { 
  simulate: true,
  speedKph: 60 
});

📄 License

MIT

🤝 Contributing

We welcome contributions! Please see our Contributing Guide.

📞 Support


Made with ❤️ for the React Native community