react-native-maplibre-lite
v0.2.7
Published
Lightweight MapLibre GL JS wrapper using WebView
Readme
react-native-maplibre-lite
Lightweight MapLibre for React Native powered by WebView.
react-native-maplibre-lite renders a bundled MapLibre GL JS runtime inside a React Native app and exposes a small React API:
MapViewMarkerPolylinePolygon
It is designed for teams that want a practical MapLibre integration without native SDK setup complexity.
Screenshots
- MapLibre GL JS in a React Native component
- Declarative overlays:
Marker,Polyline,Polygon - Built-in PMTiles protocol support
- Built-in 3D building extrusion for compatible vector styles
- Imperative camera methods:
fitBounds()andflyTo() - Optional
autoFitBoundsfor markers, polylines and polygons - Custom marker icons or marker HTML
- Optional navigator mode with GraphHopper routes, HUD, route snapping and rerouting
- Map style caching through
AsyncStorage - Android performance controls:
performanceMode,turboWhileMoving,pixelRatio
Installation
npm install react-native-maplibre-lite react-native-webview @react-native-async-storage/async-storage @sayem314/react-native-keep-awake @react-native-community/geolocation react-native-svg react-native-videoor
yarn add react-native-maplibre-lite react-native-webview @react-native-async-storage/async-storage @sayem314/react-native-keep-awake @react-native-community/geolocation react-native-svg react-native-videoreact-native-svg and react-native-video are used by the navigator UI: the
maneuver/HUD icons are drawn with SVG, and voice guidance clips are played with
react-native-video. They are only needed when you enable navigator.
Quick Start
import React, { useRef } from 'react';
import { Button, View } from 'react-native';
import MapView, {
Marker,
Polyline,
Polygon,
type MapViewRef,
} from 'react-native-maplibre-lite';
export function MapScreen() {
const mapRef = useRef<MapViewRef>(null);
return (
<View style={{ flex: 1 }}>
<MapView
ref={mapRef}
style={{ flex: 1 }}
placeholderTheme="light"
center={[37.6173, 55.7558]}
zoom={11}
mapStyle="https://demotiles.maplibre.org/style.json"
zoomEnabled
scrollEnabled
autoFitBounds
fitBoundsPadding={48}
onReady={() => console.log('map ready')}
>
<Marker
uniqueId="m-1"
latitude={55.7558}
longitude={37.6173}
color="#1D4ED8"
onPress={() => console.log('marker pressed')}
/>
<Polyline
uniqueId="line-1"
color="#2563EB"
width={4}
coordinates={[
[37.61, 55.75],
[37.63, 55.76],
[37.65, 55.74],
]}
/>
<Polygon
uniqueId="poly-1"
fillColor="#2563EB"
fillOpacity={0.15}
strokeColor="#2563EB"
strokeWidth={2}
coordinates={[
[37.6, 55.75],
[37.62, 55.77],
[37.66, 55.75],
[37.6, 55.75],
]}
/>
</MapView>
<Button
title="Fly to Moscow center"
onPress={() => mapRef.current?.flyTo([37.6173, 55.7558], 14)}
/>
</View>
);
}Coordinates use [longitude, latitude], the same order as GeoJSON and GraphHopper. Marker props still accept latitude and longitude separately.
Navigator Mode
Navigator mode adds a route line, a current-position arrow, a driving HUD and GraphHopper-based route recalculation.
The route line and position arrow are drawn inside the WebView, while the
instruction HUD, the recenter / voice floating buttons and voice playback are
rendered natively by MapView. The WebView streams a HUD view-model and
full voice phrase text to the native side; the native side renders the UI
(react-native-svg), calls your ttsHandler to obtain audio URLs and plays them
(react-native-video), owning voice selection, volume and persistence
(AsyncStorage). Tapping the
recenter button sends a command back to the WebView to recenter the camera.
import React, { useRef } from 'react';
import { Button, View } from 'react-native';
import MapView, { type MapViewRef } from 'react-native-maplibre-lite';
export function NavigatorScreen() {
const mapRef = useRef<MapViewRef>(null);
return (
<View style={{ flex: 1 }}>
<MapView
ref={mapRef}
style={{ flex: 1 }}
center={[37.6173, 55.7558]}
zoom={17}
mapStyle="https://example.com/style.json"
navigator
graphhopperUrl="https://graphhopper.example.com"
navigatorLang="ru"
navigatorProfile="bike"
navigatorChrome={{ accent: '#22c55e', routeLine: '#22c55e' }}
zoomEnabled
scrollEnabled
onNavigatorRouteSet={(route) => console.log('route', route)}
onNavigatorInstruction={(instruction) =>
console.log('next instruction', instruction)
}
onNavigatorPositionSet={(position) => console.log('position', position)}
onMapLiteError={(error) => console.warn(error)}
/>
<Button
title="Build route"
onPress={() => mapRef.current?.setNavigatorPoint(55.76, 37.64)}
/>
</View>
);
}graphhopperUrl is the base URL without the required /route suffix. The plugin sends POST {graphhopperUrl}/route with points_encoded: false, instructions: true, locale from navigatorLang, and profile from navigatorProfile (defaults to car when omitted or when the string is not a known profile; see NAVIGATOR_PROFILE_IDS in the package exports).
Use setNavigatorPosition(latitude, longitude) to feed real GPS updates. The WebView side snaps the position to the route, marks arrival, or reroutes when the point is too far from the current route. pickNavigatorPosition() is a development helper: the next tap on the map becomes the current navigator position.
Voice guidance (TTS)
Pass ttsVoices and ttsHandler on MapView to enable voice guidance and the
native voice floating button. The WebView builds localized phrase text from
embedded dictionaries (ru / en, keyed by navigatorLang) and sends the full
string to React Native. Your handler synthesizes audio and returns a URL; the
library plays it and caches URLs per voice and text.
<MapView
navigator
navigatorLang="ru"
ttsVoices={{ silero_kseniya: 'Ксения', silero_aidar: 'Айдар' }}
ttsHandler={async (text, voiceKey) => {
const res = await fetch('https://your-tts.example/speak', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text, voice: voiceKey }),
});
const { url } = (await res.json()) as { url: string };
return url;
}}
/* … */
/>See webProject/NAVIGATION_VOICE.md for phrase keys and announcement rules.
Navigator chrome (navigatorChrome)
Optional object passed as navigatorChrome on MapView. accent, routeLine
and routeOutline are forwarded to the WebView (route line and arrow); the
hud* keys theme the native HUD panel and the floating buttons. All keys are
optional; omit the prop to keep the default blue theme.
| Key | Purpose |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| accent | Accent in hex (#rgb / #rrggbb): map arrow gradient and maneuver icon tile in the HUD (tile background and icon color are derived from it). |
| routeLine | Main route line color (library default was #3b82f6). |
| routeOutline | Wide underlay for the route line (default was #1e3a8a). |
| hudBackground | HUD panel background: any valid CSS value (rgba(...), linear-gradient(...), etc.). |
| hudForeground | Primary text color on the HUD. |
| hudMuted | Secondary text (street, summary, ETA); when this is hex, the divider tone is derived from it. |
Speed limit styling on the HUD is not configurable.
Route simulation (navigatorSimulate, debug)
Pass navigatorSimulate (or call setNavigatorSimulation(true) on the ref) to
auto-drive the navigator marker along the built route. Once a route exists
(setNavigatorPoint), the WebView steps the marker along the polyline at a fixed
speed, feeding each point through the same pipeline as real GPS — so the arrow,
camera, HUD, instruction advance and voice all behave as on a real trip. This is
a dev/debug aid: while it is on, real device GPS is not forwarded to the WebView,
and the simulation stops automatically on arrival. Requires navigator: true.
API
MapView props
| Prop | Type | Required | Description |
| ---------------------------- | ------------------------------------------ | -------- | -------------------------------------------------------------------------------------- |
| center | [number, number] | yes | Initial map center as [lng, lat] |
| zoom | number | yes | Initial zoom level |
| mapStyle | string | yes | MapLibre style URL. The style JSON is fetched and cached with AsyncStorage |
| style | StyleProp<ViewStyle> | yes | Container style |
| placeholderTheme | 'light' \| 'dark' | no | Placeholder theme before map init. Defaults to light |
| minZoom | number | no | Minimum zoom |
| maxZoom | number | no | Maximum zoom |
| zoomEnabled | boolean | no | Enable double-tap zoom, pinch zoom and rotation |
| scrollEnabled | boolean | no | Enable pan and scroll gestures |
| showSelectPoint | boolean | no | Show animated center pointer |
| selectPointColor | string | no | Center pointer color |
| selectPointBackgroundColor | string | no | Center pointer background |
| autoFitBounds | boolean | no | Automatically fit camera to visible overlays after overlay changes |
| fitBoundsPadding | number | no | Padding for fitBounds() and autoFitBounds. Defaults to 40 |
| fitBoundsDuration | number | no | Animation duration for fitBounds(). Defaults to 500 |
| flyToDuration | number | no | Animation duration for flyTo(). Defaults to 500 |
| performanceMode | 'quality' \| 'balanced' \| 'performance' | no | Rendering quality/performance profile |
| pixelRatio | number | no | Manual renderer pixel ratio override |
| turboWhileMoving | boolean | no | Hide polyline/polygon overlays while map moves |
| debugMode | boolean | no | Enables extra WebView-side debug alerts/logging |
| navigator | boolean | no | Enable navigator mode |
| graphhopperUrl | string | no | Base GraphHopper URL for navigator routes |
| navigatorLang | 'ru' \| 'en' | no | Navigator HUD and instruction language. Defaults to ru |
| navigatorProfile | NavigatorProfile \| string | no | GraphHopper routing profile (car, bike, foot, …). Unknown → car |
| navigatorChrome | NavigatorChromeParams | no | Navigator colors: route line, arrow accent, native HUD/FAB theme (see section above) |
| ttsVoices | Record<string, string> | no | TTS voice key → user-visible label. With ttsHandler, enables voice guidance + FAB |
| ttsHandler | (text, voiceKey) => Promise<string> | no | Synthesize phrase text to an audio URL for playback |
| navigatorSimulate | boolean | no | Dev-only: auto-drive the marker along the built route (trip simulation). Suspends real GPS forwarding while on |
| onReady | () => void | no | Called after the WebView map is initialized |
| onMoveStart | (params) => void | no | movestart event |
| onMoveEnd | (params) => void | no | moveend event |
| onZoomStart | (params) => void | no | zoomstart event |
| onZoomEnd | (params) => void | no | zoomend event |
| onIdle | (params) => void | no | idle event |
| onNavigatorRouteSet | (params) => void | no | Called after navigator route creation |
| onNavigatorInstruction | (params) => void | no | Called when a navigator instruction is advanced |
| onNavigatorPositionSet | (params) => void | no | Called after navigator position update/snap/reroute |
| onMapLiteError | (error) => void | no | WebView command error callback |
| useNativeMapHtml | boolean | no | Load map.html from native app resources instead of inline MAP_HTML. See below |
| developerLocalhostBundleUrl| string | no | Dev-only: load WebView bundle from a local URL instead of inline or native map.html |
MapView ref
| Method | Description |
| ------------------------------------------- | ----------------------------------------------------------------- |
| fitBounds() | Fits camera to current markers, polylines and polygons |
| flyTo(center, zoom) | Animates camera to [lng, lat] and zoom |
| setNavigatorPoint(latitude, longitude) | Builds a navigator route to the destination. Requires navigator |
| advanceNavigatorInstruction() | Advances to the next navigator instruction |
| setNavigatorPosition(latitude, longitude) | Updates current navigator position from GPS or another source |
| pickNavigatorPosition() | Dev helper: next map tap sets current navigator position |
| setNavigatorSimulation(enabled) | Dev helper: start/stop the route trip simulation (same as navigatorSimulate prop) |
Marker props
| Prop | Type | Required | Description |
| ----------------- | ------------ | -------- | ----------------------------------------------------------------------------- |
| uniqueId | string | yes | Unique overlay id |
| latitude | number | yes | Marker latitude |
| longitude | number | yes | Marker longitude |
| onPress | () => void | no | Press callback |
| ignoreFitBounds | boolean | no | Exclude marker from fitBounds() and autoFitBounds |
| color | string | no | Default MapLibre marker color |
| iconUrl | string | no | Custom marker image URL |
| iconWidth | number | no | Custom icon width |
| iconHeight | number | no | Custom icon height |
| html | string | no | Custom marker HTML. If provided, it takes priority over iconUrl and color |
Polyline props
| Prop | Type | Required | Description |
| ----------------- | -------------------- | -------- | --------------------------------------------------- |
| uniqueId | string | yes | Unique overlay id |
| coordinates | [number, number][] | yes | Line coordinates as [lng, lat] |
| ignoreFitBounds | boolean | no | Exclude line from fitBounds() and autoFitBounds |
| color | string | no | Line color. Defaults to #000000 |
| width | number | no | Line width. Defaults to 4 |
Polygon props
| Prop | Type | Required | Description |
| ----------------- | -------------------- | -------- | ------------------------------------------------------ |
| uniqueId | string | yes | Unique overlay id |
| coordinates | [number, number][] | yes | Polygon ring coordinates as [lng, lat] |
| ignoreFitBounds | boolean | no | Exclude polygon from fitBounds() and autoFitBounds |
| fillColor | string | no | Fill color |
| fillOpacity | number | no | Fill opacity |
| strokeColor | string | no | Stroke color |
| strokeOpacity | number | no | Stroke opacity |
| strokeWidth | number | no | Stroke width |
Performance Tuning (Android)
Start with:
<MapView
// ...
performanceMode="performance"
turboWhileMoving
/>Profiles:
quality: best visual qualitybalanced: default on Android; better FPS with moderate quality reductionperformance: aggressive optimizations, lower renderer resolution, simplified style and reduced map effects
Tips:
- If you still see frame drops, pass a lower
pixelRatio, for example0.75 - Keep number of simultaneously visible overlays moderate
- Prefer simpler map styles with fewer labels and 3D layers
Native map.html bundle
By default, MapView loads the map runtime as inline HTML from the generated webMapBuild.ts bundle (MAP_HTML). For smaller JS bundle size or to update the WebView runtime without republishing the npm package, copy resources/map.html into your native app and enable useNativeMapHtml:
<MapView
useNativeMapHtml
style={{ flex: 1 }}
center={[37.6173, 55.7558]}
zoom={11}
mapStyle="https://demotiles.maplibre.org/style.json"
/>WebView source priority: developerLocalhostBundleUrl → useNativeMapHtml → inline MAP_HTML.
Android
Copy map.html from the package (node_modules/react-native-maplibre-lite/resources/map.html) or from this repo after building webProject:
android/app/src/main/assets/map.htmlCreate the assets folder if it does not exist. MapView loads it as file:///android_asset/map.html.
iOS
Copy map.html into your app project, for example:
ios/MyApp/map.htmlIn Xcode:
- Drag
map.htmlinto the project tree (not only into Finder). - Enable Copy items if needed and select your app target.
- Confirm the file appears under Build Phases → Copy Bundle Resources.
The URI is resolved automatically from the .app bundle path (release / standalone builds). In Metro dev builds on iOS, prefer the default inline HTML or developerLocalhostBundleUrl.
Updating map.html
After changes in webProject, rebuild and recopy the file into both platforms:
cd webProject
npm run build
cp ../resources/map.html android/app/src/main/assets/map.html
cp ../resources/map.html ios/MyApp/map.htmlSee also resources/README.md.
Web Bundle Development
The WebView runtime is generated from webProject and committed as:
src/components/webMapBuild.ts— inlineMAP_HTMLused byMapViewby defaultresources/map.html— same single-file bundle for native app resources (useNativeMapHtml)
cd webProject
npm install
npm run buildnpm run build runs Vite and then webProject/scripts/inlineHtml.mjs, which writes both webMapBuild.ts and resources/map.html.
Contributing
See the contributing guide for local setup and development workflow.
License
MIT
