Migrating to v11
MapLibre React Native v11 is the first release to only support the new architecture. As this required significant changes to the implementation, the APIs were also improved and are based upon MapLibre GL JS.
Changes to Map Component
The MapView was renamed to Map to align with MapLibre GL JS.
-<MapView
+<Map
mapStyle="https://demotiles.maplibre.org/style.json"
/>
Android View uses GLSurfaceView by default
On Android the Map uses GLSurfaceView by default instead of TextureView for native rendering. This aligns
with the default of the native SDKs, this switch
is pre-dating MapLibre and was never
propagated to the React
Native wrapper. To fall back to the TextureView use the androidView="texture" prop.
Removed Props
The following props have been removed from the Map without replacement:
localizeLabels: This was used in conjunction with Mapbox specific map styles for toggling localization, MapLibre- aims to be vendor-agnostic and the prop has therefore been removed
regionWillChangeDebounceTime®ionDidChangeDebounceTime: Debouncing should be handled in the app code if needed
Ornament Props
All ornaments (attribution, logo, compass) use an aligned API. Enable/disable the visibility with the
attribution/logo/compass boolean props. Position them with the
attributionPosition/logoPosition/compassPosition props which only accepts an object with top/bottom and
left/right properties.
contentInset
Uses unified ViewPadding type:
<Map
- contentInset={[16, 16, 16, 16]}
+ contentInset={{ top: 16, right: 16, bottom: 16, left: 16 }}
/>
Interaction Props
Interaction props have been aligned with the MapLibre GL JS API as far as possible.
<Map
- scrollEnabled={false}
+ dragPan={false}
- zoomEnabled={false}
+ touchZoom={false}
+ doubleTapZoom={false}
+ doubleTapHoldZoom={false}
- rotateEnabled={false}
+ touchRotate={false}
- pitchEnabled={false}
+ touchPitch={false}
/>
Events
Events follow the pattern of NativeSyntheticEvent and passes the payload in the nativeEvent property.
Imperative Methods on MapRef
Imperative methods have been aligned with the MapLibre GL JS API as far as possible.
- Renamed methods:
getVisibleBounds()->getBounds()getPointInView()->project()getCoordinateFromView()->unproject()takeSnap()->createStaticMapImage()
- Unified methods:
queryRenderedFeaturesAtPoint()&queryRenderedFeaturesInRect()->queryRenderedFeatures()- Instead of passing pixel coordinates, geographic coordinates are being required to query
- More complete API by adding:
getBearing()getPitch()getViewState()
Light Component removed
The Light component has been removed. Use the light prop on Map instead.
<Map
+ light={{
+ position: [1.5, 90, 80],
+ color: "#ffffff",
+ intensity: 0.5,
+ }}
>
- <Light
- style={{
- position: [1.5, 90, 80],
- color: "#ffffff",
- intensity: 0.5,
- }}
- />
</Map>
Changes to Camera Component
Changes to Camera Stop Props
The prop was renamed and the structure was aligned with MapLibre GL JS and react-map-gl:
<Camera
- centerCoordinate={[longitude, latitude]}
+ center={[ longitude, latitude ]}
- zoomLevel={10}
+ zoom={10}
- heading={15}
+ bearing={15}
pitch={30}
- animationDuration={2000}
+ duration={2000}
- animationMode="flyTo"
+ easing="fly"
/>
String Literal CameraAnimationMode has been renamed to CameraEasing and changed as follows:
moveTo->undefinedlinearTo->lineareaseTo->easeflyTo->fly
Prop defaultSettings changed to initialViewState
The prop was renamed and the structure was aligned with MapLibre GL JS and react-map-gl:
<Camera
- defaultSettings={{
+ initialViewState={{
- centerCoordinate: [longitude, latitude],
+ center: [longitude, latitude],
- zoomLevel: 10,
+ zoom: 10,
- heading: 15,
+ bearing: 15
pitch: 30,
- bounds: {
- ne: [east, north],
- sw: [west, south],
- paddingTop: 16,
- paddingRight: 16,
- paddingBottom: 16,
- paddingLeft: 16,
- },
+ bounds: [west, south, east, north],
+ padding: { top: 16, right: 16, bottom: 16, left: 16 },
}}
/>
Follow Prop Changes
The followUserLocation and followUserMode prop has been unified to trackUserLocation.
The enum UserTrackingMode has been replaced by the string literal TrackUserLocation:
normal->defaultcompass->headingcourse->course
The follow props to control the camera during tracking have been removed in favor of the default camera stop props:
<Camera
- followUserLocation
- followUserMode="normal"
+ trackUserLocation="default"
- followZoomLevel={15}
+ zoom={15}
- followHeading={0}
+ bearing={0}
- followPitch={45}
+ pitch={45}
/>
Imperative Methods on CameraRef
Imperative methods have been aligned with the MapLibre GL JS API as far as possible.
setCamera()->setStop()moveTo()->jumpTo()- More complete API by adding:
easeTo()
Changes to Source Components
Props
Optional id Prop
The id prop is optional and will be auto-generated if not provided:
<GeoJSONSource
- id="my-source"
+ id="my-source"
data={geojson}
/>
tiles, minzoom, maxzoom, scheme Props (only RasterSource & VectorSource)
Tiled sources now use MapLibre Style spec aligned props:
<VectorSource
- tileUrlTemplates={['https://example.com/tiles/{z}/{x}/{y}.pbf']}
+ tiles={['https://example.com/tiles/{z}/{x}/{y}.pbf']}
- minZoomLevel={0}
+ minzoom={0}
- maxZoomLevel={14}
+ maxzoom={14}
- tms
+ scheme="tms"
/>
hitbox Structure Changed (only GeoJSONSource & VectorSource)
The hitbox prop uses the unified ViewPadding type instead of width/height:
<VectorSource
- hitbox={{ width: 44, height: 44 }}
+ hitbox={{ top: 22, right: 22, bottom: 22, left: 22 }}
/>
Events (only GeoJSONSource & VectorSource)
The onPress event follows the NativeSyntheticEvent pattern with the payload in nativeEvent:
<VectorSource
onPress={(event) => {
- const { features, coordinates, point } = event;
+ const { features, lngLat, point } = event.nativeEvent;
}}
/>
Also onPress bubbles up to Map by default. To prevent this, call event.stopPropagation().
Specific Changes to GeoJSONSource Component
The ShapeSource component has been renamed to GeoJSONSource to align with MapLibre GL JS terminology:
-<ShapeSource
+<GeoJSONSource
data={geojson}
/>
Unified data Prop
The separate url and shape props have been unified into a single data, to align with MapLibre GL JS. The data
prop accepts both URL strings and GeoJSON objects:
<GeoJSONSource
- url="https://example.com/data.geojson"
+ data="https://example.com/data.geojson"
/>
<GeoJSONSource
- shape={geojson}
+ data={geojson}
/>
Renamed Cluster Props
Several props have been renamed to align with the MapLibre Style Spec and MapLibre GL JS:
<GeoJSONSource
- clusterMaxZoomLevel={14}
+ clusterMaxZoom={14}
/>
Imperative Methods on GeoJSONSourceRef
Several imperative methods have been renamed and simplified to align with MapLibre GL JS:
features() → getData()
The method has been renamed:
-const featureCollection = await geoJSONSourceRef.current.features(filter);
+const featureCollection = await geoJSONSourceRef.current.getData(filter);
Due to MapLibre Native limitations this is not the original data but always a FeatureCollection. For example a
GeometryCollection will always be transformed to a FeatureCollection.
Cluster Methods use clusterId Parameter instead of GeoJSON Feature
All cluster-related methods accept a clusterId number instead of a GeoJSON Feature to align with MapLibre GL JS. The
clusterId can be obtained from the cluster_id property of a cluster feature:
+const clusterId = clusterFeature.properties.cluster_id;
-const zoom = await geoJSONSourceRef.current.getClusterExpansionZoom(clusterFeature);
+const zoom = await geoJSONSourceRef.current.getClusterExpansionZoom(clusterId);
-const leaves = await geoJSONSourceRef.current.getClusterLeaves(clusterFeature, 10, 0);
+const leaves = await geoJSONSourceRef.current.getClusterLeaves(clusterId, 10, 0);
-const children = await geoJSONSourceRef.current.getClusterChildren(clusterFeature);
+const children = await geoJSONSourceRef.current.getClusterChildren(clusterId);
Return Types
The cluster methods return GeoJSON.Feature[] instead of GeoJSON.FeatureCollection:
-const leaves: GeoJSON.FeatureCollection = await geoJSONSourceRef.current.getClusterLeaves(clusterId, 10, 0);
+const leaves: GeoJSON.Feature[] = await geoJSONSourceRef.current.getClusterLeaves(clusterId, 10, 0);
Specific Changes to VectorSource Component
Imperative Methods on VectorSourceRef
The imperative method has been renamed and simplified to align with MapLibre GL JS:
features() → querySourceFeatures()
The method has been renamed, parameters support only one source layer, get passed as and object and the return type
changed to a GeoJSON.Feature[]:
-const featureCollection: GeoJSON.FeatureCollection = await vectorSourceRef.current.features([sourceLayer], filter);
+const features: GeoJSON.Feature[] = await vectorSourceRef.current.querySourceFeatures({ sourceLayer, filter });
Changes to Layer Component
Unified Layer Component
All specific layer components (FillLayer, LineLayer, CircleLayer, SymbolLayer, HeatmapLayer,
FillExtrusionLayer, RasterLayer, BackgroundLayer) have been consolidated into a single Layer
component. Use the type prop to specify:
-<FillLayer
+<Layer
+ type="fill"
style={{ fillColor: 'blue' }}
/>
-<LineLayer
+<Layer
+ type="line"
style={{ lineColor: 'red', lineWidth: 2 }}
/>
-<CircleLayer
+<Layer
+ type="circle"
style={{ circleRadius: 10, circleColor: 'green' }}
/>
Renamed Layer Props
Several props have been renamed to align with MapLibre Style spec:
<Layer
type="fill"
- sourceID="my-source"
+ source="my-source"
- sourceLayerID="my-layer"
+ source-layer="my-layer"
- belowLayerID="other-layer"
+ beforeId="other-layer"
- aboveLayerID="other-layer"
+ afterId="other-layer"
- minZoomLevel={10}
+ minzoom={10}
- maxZoomLevel={18}
+ maxzoom={18}
style={{ fillColor: 'blue' }}
/>
Animated now also only provides Animated.Layer.
New Style Spec Compliant paint and layout Props
The Layer component now supports style-spec compliant paint and layout props with kebab-case property names. This enables:
- Direct use of layer definitions from Maputnik or other style editors
- Sharing layer styles between web (maplibre-gl-js) and native platforms
- Loading layer configurations from APIs or JSON files
- Type-safe autocomplete using
LayerSpecificationfrom@maplibre/maplibre-gl-style-spec
<Layer
type="fill"
id="parks"
source="parks-source"
- style={{
- fillColor: "green",
- fillOpacity: 0.5,
- visibility: "visible",
- }}
+ paint={{
+ "fill-color": "green",
+ "fill-opacity": 0.5,
+ }}
+ layout={{
+ visibility: "visible",
+ }}
/>
The style prop is deprecated but still works for backwards compatibility. It will be removed in v12.
JSON Interoperability Example
Layer definitions can now be loaded directly from JSON or APIs:
import {
Layer,
type LayerSpecification,
} from "@maplibre/maplibre-react-native";
// Loaded from API, Maputnik export, or shared web/native codebase
const layerDefinition: LayerSpecification = {
id: "parks-fill",
type: "fill",
source: "parks",
paint: {
"fill-color": "#00ff00",
"fill-opacity": 0.5,
},
};
// Spread directly onto Layer component!
<Layer {...layerDefinition} />;
Changes to Images Component
Unified images Prop
The separate images and nativeAssetImages props have been unified into a single images prop. Native assets are now specified using the same object structure as other image types:
<Images
- images={{
- remote: 'https://example.com/marker.png',
- require: require('./marker.png'),
- }}
- nativeAssetImages={['pin', 'marker']}
+ images={{
+ remote: 'https://example.com/marker.png',
+ require: require('./marker.png'),
+ pin: 'pin',
+ marker: 'marker',
+ }}
/>
Simplified ImageEntry Type
The ImageEntry type has been simplified. If you need to use ImageURISource, set it via the source property:
<Images
images={{
- icon: { uri: 'https://example.com/icon.png' },
+ icon: { source: { uri: 'https://example.com/icon.png' } },
}}
/>
Changed onImageMissing Event Structure
The onImageMissing event now follows the NativeSyntheticEvent pattern with the image name in nativeEvent:
<Images
images={images}
- onImageMissing={(image) => {
- console.log('Missing image:', image);
+ onImageMissing={(event) => {
+ console.log('Missing image:', event.nativeEvent.image);
}}
/>
Removed id and children Props
The id and children props have been removed:
<Images
- id="my-images"
images={images}
-/>
- {children}
-</Images>
+/>
Changes to LayerAnnotation Component
The Annotation component has been renamed to LayerAnnotation to better reflect its purpose:
Renamed coordinates Prop to lngLat
The coordinates prop has been renamed to lngLat to align with the rest of the library's terminology:
<LayerAnnotation
- coordinates={[longitude, latitude]}
+ lngLat={[longitude, latitude]}
/>
Removed style and icon Props
The style and icon props have been removed. To display an icon or custom styling, add a Layer component as a child:
-<Annotation
- coordinates={[longitude, latitude]}
- icon="pin"
- style={{ iconSize: 1.5 }}
-/>
+<LayerAnnotation
+ lngLat={[longitude, latitude]}
+>
+ <Layer
+ type="symbol"
+ style={{ iconImage: 'pin', iconSize: 1.5 }}
+ />
+</LayerAnnotation>
Changed onPress Event Structure
The onPress event now follows the NativeSyntheticEvent pattern with the payload in nativeEvent:
<LayerAnnotation
lngLat={[longitude, latitude]}
onPress={(event) => {
- const { features, coordinates, point } = event;
+ const { features, lngLat, point } = event.nativeEvent;
}}
/>
Changes to ViewAnnotation Component
The PointAnnotation component has been renamed to ViewAnnotation.
-<PointAnnotation
+<ViewAnnotation
lngLat={[longitude, latitude]}
/>
Optional id Prop
The id prop is now optional and will be auto-generated if not provided.
Renamed coordinate Prop to lngLat
-<PointAnnotation
+<ViewAnnotation
- coordinate={[longitude, latitude]}
+ lngLat={[longitude, latitude]}
/>
Changed anchor Prop
The anchor prop changed from a numeric { x, y } object to a string anchor position aligned with MapLibre GL JS:
-<PointAnnotation
+<ViewAnnotation
lngLat={[longitude, latitude]}
- anchor={{ x: 0.5, y: 0 }}
+ anchor="top"
/>
Added offset Prop
A new offset prop has been added for pixel-level positioning relative to the anchor:
<ViewAnnotation lngLat={[longitude, latitude]} offset={[10, -5]} />
Renamed Ref Type
-const ref = useRef<PointAnnotationRef>(null);
+const ref = useRef<ViewAnnotationRef>(null);
Event Callbacks Renamed and Restructured
All event callbacks now follow the NativeSyntheticEvent pattern with the payload in nativeEvent. The payload
changed from a GeoJSON Feature to a structured event object with id, lngLat, and point fields.
onSelected and onDeselected were also renamed to onSelect and onDeselect to align with common terminology:
-<PointAnnotation
+<ViewAnnotation
lngLat={[longitude, latitude]}
- onSelected={(payload) => {
- const [longitude, latitude] = payload.geometry.coordinates;
- const { screenPointX, screenPointY } = payload.properties;
- }}
+ onSelect={(event) => {
+ const { id, lngLat, point } = event.nativeEvent;
+ }}
- onDeselected={(payload) => {}}
+ onDeselect={(event) => {}}
/>
Changes to LocationManager Module
GeolocationPosition Type
The LocationManager module emits GeolocationPosition type, renamed from Location type. It also reassembles the
web GeolocationPosition much more closely.
Previously there was partial support for heading and course. This has been unified as defined by the spec to
heading which gives the direction of travel on both platforms. Retrieving the compass direction is not supported due
to native limitations.
Incorporation of requestAndroidPermissions into unified requestPermissions
The LocationManager incorporates a requestPermissions function, which supports Android and iOS. The standalone
requestAndroidPermissions function has been removed:
-requestAndroidPermissions()
+LocationManager.requestPermissions()
Changes to UserLocation Component
Replaced onUpdate with useCurrentPosition Hook
The onUpdate prop has been removed in favor of the new useCurrentPosition hook for accessing the user location data:
+const position = useCurrentPosition();
return (
<UserLocation
- onUpdate={(position) => {
- // ...
- }}
/>
)
Added the accuracy Prop
To show the accuracy circle around the user location set the accuracy prop to true. This replaces the previous
circle that didn't have any meaning:
<UserLocation
+ accuracy
/>
Separation of NativeUserLocation
The NativeUserLocation, which previously was used for renderMode="native", has been branched out as a separate
component:
-<UserLocation renderMode="native" />
+<NativeUserLocation />
This component only relies on the default MapLibre Native user location implementations without any custom React Native customizations.
Changes to OfflineManager Module
OfflinePack Identification changed from name to id
Offline packs are now identified by a UUID-based id instead of a user-provided name. The id is automatically
generated when creating a pack:
-const pack = await OfflineManager.getPack(pack.name);
+const pack = await OfflineManager.getPack(pack.id);
-await OfflineManager.deletePack(pack.name);
+await OfflineManager.deletePack(pack.id);
-await OfflineManager.invalidatePack(pack.name);
+await OfflineManager.invalidatePack(pack.id);
OfflinePacks from previous versions will have their name identifier within pack.metadata.name.
Changes to createPack
Returns OfflinePack
The createPack method now returns the resulting OfflinePack to provide the generated id:
+const progressListener = (offlinePack, status) => console.log(offlinePack, status);
+const errorListener = (offlinePack, error) => console.log(offlinePack, error);
+
-await OfflineManager.createPack(
+const offlinePack = await OfflineManager.createPack(
options,
progressListener,
errorListener,
);
+
+console.log('Pack ID:', offlinePack.id);
Adapted OfflinePackCreateOptions
The options object for creating offline packs has been restructured:
const offlinePack = await OfflineManager.createPack({
- styleURL: 'https://demotiles.maplibre.org/tiles/tiles.json',
+ mapStyle: 'https://demotiles.maplibre.org/tiles/tiles.json',
minZoom: 14,
maxZoom: 20,
- bounds: [[east, north], [west, south]],
+ bounds: [west, south, east, north],
- name: 'Name',
+ metadata: { name: 'Name' },
}, progressListener, errorListener);
subscribe/unsubscribe Renamed to addListener/removeListener
The subscription methods have been renamed for consistency:
-await OfflineManager.subscribe(pack.name, progressListener, errorListener);
+await OfflineManager.addListener(pack.id, progressListener, errorListener);
-OfflineManager.unsubscribe(pack.name);
+OfflineManager.removeListener(pack.id);
Event Payload Structure Changed
Progress and error events now use the pack id instead of name:
const progressListener = (offlinePack, status) => {
- const { name, state, percentage, completedResourceCount } = status;
+ const { id, state, percentage, completedResourceCount } = status;
};
const errorListener = (offlinePack, error) => {
- const { name, message } = error;
+ const { id, message } = error;
};
Change to StaticMapManager Module
The SnapshotManager has been renamed to StaticMapManager to align with common geospatial terminology. The
createImage methods now uses strict types, aligned with other components:
const uri = await StaticMapManager.createImage({
- centerCoordinate: [-74.12641, 40.797968],
+ center: [-74.12641, 40.797968],
- zoomLevel: 12,
+ zoom: 12,
- heading: 20,
+ bearing: 20,
pitch: 30,
- styleURL: 'https://demotiles.maplibre.org/tiles/tiles.json',
+ mapStyle: 'https://demotiles.maplibre.org/tiles/tiles.json',
width: width,
height: height,
- writeToDisk: true,
+ output: 'file',
});
Changes to LogManager Module
The Logging module has been renamed to LogManager for consistency with other modules.
If you want to implement a custom log handler, use the onLog function instead of setLogCallback:
-Logging.setLogCallback((event) => {
+LogManager.onLog((event) => {
console.log(event.message);
return true;
});
The LogLevel warning has been renamed to warn to match console.warn.
Removal of MLRNModule
The MLRNModule has been removed, some of it functionality has been moved to other modules.
Constants Removed
The following constants have been removed:
CameraModes,OfflinePackDownloadState: Replaced by string literalsStyleSource.DefaultSourceID: No longer needed with auto-generated source IDsStyleURL.Default: No replacement, provide your own map styles
Network Methods moved to TransformRequestManager and NetworkManager
Adding HTTP headers moved to TransformRequestManager and network connectivity controls are now managed through the NetworkManager:
-addCustomHeader('Authorization', 'Bearer token123');
+TransformRequestManager.addHeader({ id: 'auth', name: 'Authorization', value: 'Bearer token123' });
-removeCustomHeader('Authorization');
+TransformRequestManager.removeHeader('auth');
-setConnected(false);
+NetworkManager.setConnected(false);
The native setup for iOS is no longer required. TransformRequestManager methods work out of the box.
Access Token Methods Removed
The deprecated setAccessToken and getAccessToken methods have been removed without replacement. Access tokens should
be added through URLs or request headers.