# react-native-maps: Native Map Views for React Native on iOS and Android

> react-native-maps renders Apple Maps or Google Maps inside a React Native app and exposes markers, polygons and tile overlays as ordinary React children. It is a native module, so the cost is in the install, not the API.

**react-native-maps/react-native-maps** — React Native Mapview component for iOS + Android

- Repository: https://github.com/react-native-maps/react-native-maps
- Stars: 16,000 · Forks: 4,960
- Language: TypeScript
- License: MIT
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/react-native-maps-react-native-maps

## What react-native-maps is for, and who ends up using it

The package answers a narrow question: how do I put the map the operating system already has into a React Native screen? On iOS that is MapKit, on Android it is Google Maps. The README describes the component as built so that map features such as markers and polygons are specified as children of the MapView itself, which is the whole design idea. You do not imperatively add pins to a map object. You write JSX and the native view reconciles it.

That makes it a fit for teams already shipping a bare React Native app where the map is one screen among many: store locators, delivery tracking, field data collection, anything where the map must behave like the rest of the app. It is a poorer fit for a prototype that lives entirely inside Expo Go, because the package ships native iOS and Android code. The peer dependency block in package.json asks for react 18.3.1 or newer and react-native 0.76.0 or newer, and react-native-web is listed as optional. If your app is below those floors, this is not a version negotiation you can win by editing package.json alone.

## How the component model maps onto native views

Everything visible on the map is a child element. Marker, Callout, Polygon, Polyline, Circle, Overlay, Heatmap, Geojson and the tile overlays each have their own document under docs/, and the README links to each one. The reconciliation is the mechanism: React Native mounts the native map view, then mounts each child as a corresponding native annotation or shape.

Region handling shows the two modes clearly. With initialRegion the map is uncontrolled and the native view owns the camera after mount. With region plus onRegionChange the camera is state, and every pan or zoom round-trips through JavaScript before the map settles. That second mode is where you feel the bridge, and it is the reason a controlled region on a low-end Android device can stutter. The README gives both forms without picking a winner, which is fair, but the uncontrolled form is the one that behaves like a native map.

Tile overlays extend the same model. UrlTile takes a urlTemplate where {x}, {y} and {z} are substituted at runtime, and maximumZ is documented as iOS only. LocalTile takes a pathTemplate and a tileSize, usually 256 or 512. The README is explicit that on Android LocalTile is still an overlay over the original map tiles, so if the device is online the underlying tiles are downloaded anyway. The documented workaround is to set mapType to none on Android while leaving iOS on standard.

## Installing react-native-maps and drawing a first map

The README does not carry install steps inline. It points at docs/installation.md and at docs/examples-setup.md for the bundled example project, so those two files are the real instructions and the ones to read before touching your own project. What the repository does show is the shape of the install: there is an android/ directory, an ios/ directory, a react-native-maps.podspec and a react-native.config.js, which is the layout of a package with native code on both platforms.

The package itself comes from npm under the name react-native-maps:

```bash
npm install react-native-maps
```

On the library side, the repository defines a bootstrap script that installs the example app and its pods in one go. It is a developer convenience for working on the library, not a step for consumers, but it shows the two halves of the setup: JavaScript dependencies and CocoaPods.

```bash
yarn bootstrap
```

Once the native side is linked, the smallest working screen is the initialRegion example from the README. The map appears at that camera position and stays there until the user moves it.

```jsx
<MapView
  initialRegion={{
    latitude: 37.78825,
    longitude: -122.4324,
    latitudeDelta: 0.0922,
    longitudeDelta: 0.0421,
  }}
/>
```

Markers are then children of that view. The README's list example maps an array into Marker elements with a coordinate, a title and a description, which is what produces the pin and its callout text.

```jsx
import {Marker} from 'react-native-maps';

<MapView region={this.state.region} onRegionChange={this.onRegionChange}>
  {this.state.markers.map((marker, index) => (
    <Marker
      key={index}
      coordinate={marker.latlng}
      title={marker.title}
      description={marker.description}
    />
  ))}
</MapView>
```

If you add a UrlTile overlay on Android, the README says to put the internet permission in AndroidManifest.xml. On iOS the equivalent step is configuring App Transport Security.

## Custom markers, and the performance cliff the README admits

Marker customisation comes in two forms and the documentation is unusually blunt about the difference. A custom image is the cheap path: generate a png at the resolutions you need, drop it into the Android drawables and iOS assets directories, then pass image={{uri: 'custom_pin'}}. The README notes you can also pass binary data with require('custom_pin.png'), but warns it will not scale well across screen sizes.

The other path is a custom view as a Marker child. The README's note on this is the most useful sentence in the file: it has performance implications, and if you want a simpler solution, go with a custom image. That is not marketing hedging, it is a real constraint. A custom marker view means a React Native view tree is mounted per pin, and a screen with a few hundred pins will show it. Callouts follow the same rule: a Callout child wrapping your own component is a view tree, not a native callout.

Draggable markers are supported through the draggable prop with an onDragEnd handler that reads e.nativeEvent.coordinate. The README's example writes that coordinate straight into state, which is fine for one pin and a poor pattern for many, since each drag end triggers a render of the map's children.

## Where react-native-maps is the wrong choice

The clearest failure mode is Expo Go. The package contains native iOS and Android code, so it cannot run in a client that only loads JavaScript. You need a development build or a bare workflow. The RELATED search phrases around Expo maps point at exactly this confusion, and there is a separate maps package in the Expo ecosystem for the managed case. If your team's whole workflow depends on Expo Go, this library is not the one to fight with.

The second boundary is the renderer itself. This project wraps the platform map. It does not draw tiles in JavaScript. If your requirement is identical map rendering on iOS, Android and web from one codebase, or a renderer you can inspect and patch in JS, a JavaScript map library is a different product with a different trade-off, and the platform-native path will not get you there.

The third is offline. The README documents LocalTile for locally stored tiles, and immediately qualifies it on Android: LocalTile is still an overlay over the original map tiles, so online devices still download the base tiles. The suggested mapType of none on Android is a workaround, not an offline mode. If your users need a map with no network at all, the documentation here does not promise that.

Finally, the README says the project is maintained by a small group of people and asks for help with issues and pull requests. That is a maintenance statement worth reading literally when you plan a long support horizon.

## How it compares with a JavaScript map renderer

The realistic alternative is a JavaScript map library that renders tiles and vector data itself rather than delegating to MapKit or Google Maps. The difference is architectural, not cosmetic.

With react-native-maps, gestures, animation and tile loading happen in the native map view, and JavaScript only sees the events you subscribe to. That is why panning stays smooth even when your JS thread is busy. The cost is that you inherit the platform's behaviour, its styling limits and its native dependencies. With a JavaScript renderer, the map is your code: you can restyle anything, run the same rendering logic on web, and ship without a native map SDK. The cost is that every gesture and every frame goes through the JavaScript thread, and a busy app shows it on the map.

There is a middle option inside this library for teams that need custom tiles: UrlTile and LocalTile let you point the native map at your own tile server with a {x}/{y}/{z} template, which keeps native rendering while replacing the base layer. That is often enough to avoid a full switch.

## Version compatibility, licence and what upgrades cost

The compatibility table is the most consequential part of the README for anyone planning an upgrade. On the new architecture, version 1.26.1 and above require React Native 0.81.1 or newer, while 1.26.0 and below require 0.76. On the old architecture, 1.14.0 through 1.20.1 require 0.74 or newer, and anything below 1.14.0 requires 0.64.3. Those are hard floors. A React Native upgrade and a react-native-maps upgrade are therefore coupled, and the table tells you which direction to move first.

The repository also shows the release machinery. package.json defines a release script running semantic-release, and .releaserc.json sits at the top level. Releases are frequent and versioned, which means the changelog, not the README, is where behaviour changes land. CHANGELOG.md is in the repository root.

The licence is MIT, stated in package.json and in the LICENSE file. MIT is permissive, but that is a statement about the library's terms. Your own obligations come from the map provider you select: Apple's MapKit terms on iOS, and Google Maps terms plus API key configuration on Android, are separate agreements. Nothing in this repository's licence changes those, and this is not legal advice.

Upgrade cost is dominated by the native side. Because the package ships android/ and ios/ directories and a podspec, an upgrade means reinstalling pods and rebuilding both platforms, not just bumping a version in package.json. The repository keeps a Detox configuration and an e2e directory, which hints at how the maintainers validate native behaviour, but that is their pipeline, not yours.

## Conclusion

Adopt react-native-maps when you already ship a bare React Native app and need the platform map view rather than a third-party renderer. Skip it if you only build through Expo Go, since the package ships native code that Expo Go cannot load, or if you want a JavaScript renderer you can keep in one process. Before committing, confirm your React Native version against the compatibility table: 1.26.1 and above require 0.81.1 or newer on the new architecture, while 1.26.0 and below require 0.76. Then read docs/installation.md, because the README itself only links to it.

## FAQ

### What is react-native-maps?

It is a React Native component library that renders MapKit on iOS and Google Maps on Android. Map features such as markers, polygons and tile overlays are declared as children of the MapView element.

### How do I install react-native-maps?

The package installs from npm as react-native-maps, but the README does not list the full steps inline. It points to docs/installation.md for the installation instructions and docs/examples-setup.md for the bundled example project.

### How do I use react-native-maps in Expo?

The README does not document an Expo workflow, and the package contains native iOS and Android code, so it cannot run in a JavaScript-only client such as Expo Go. A development build or bare workflow is required.

### Is react-native-maps free?

The library itself is MIT licensed, as stated in package.json and the LICENSE file. That covers the library only. The map provider you use on each platform has its own terms, and Google Maps on Android requires its own API key configuration.

### How does react-native-maps compare with a JavaScript map renderer?

react-native-maps delegates rendering and gestures to the platform map view, so panning stays off the JavaScript thread. A JavaScript renderer draws the map itself, which gives you full control over styling and web parity but puts every frame through the JavaScript thread.

## Sources

- [Issues](https://github.com/react-native-maps/react-native-maps/issues)
- [License: MIT](https://github.com/react-native-maps/react-native-maps/blob/master/LICENSE)
- [react-native-maps/react-native-maps on GitHub](https://github.com/react-native-maps/react-native-maps)
- [README](https://github.com/react-native-maps/react-native-maps/blob/master/README.md)
- [Releases](https://github.com/react-native-maps/react-native-maps/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/react-native-maps-react-native-maps
